├── examples ├── dsp │ ├── mod.rs │ ├── osc.rs │ └── wavetable.rs ├── utilities │ ├── mod.rs │ └── logger.rs ├── blinky_pac.rs ├── blinky_hal.rs ├── blinky.rs ├── itm.rs ├── button.rs ├── ethernet.rs └── ethernet_hal.rs ├── .gitignore ├── openocd.cfg ├── .cargo └── config.toml ├── swo.cfg ├── openocd.gdb ├── testsuite ├── Makefile ├── Cargo.toml ├── src │ └── lib.rs └── tests │ └── unit.rs ├── README.md ├── LICENSE ├── src ├── lib.rs ├── led.rs ├── itm.rs ├── timer.rs ├── board.rs ├── clocks.rs ├── pins.rs └── ethernet.rs ├── CHANGELOG.md ├── memory.x └── Cargo.toml /examples/dsp/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod osc; 2 | pub mod wavetable; 3 | -------------------------------------------------------------------------------- /examples/utilities/mod.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for examples 2 | 3 | pub mod logger; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | 5 | # misc 6 | /Makefile 7 | RELEASE.md 8 | TODO.md 9 | reference/ 10 | examples.off 11 | -------------------------------------------------------------------------------- /openocd.cfg: -------------------------------------------------------------------------------- 1 | # semi-hosting support doesn't work 2 | #source [find board/st_nucleo_h745zi.cfg] 3 | source [find interface/stlink.cfg] 4 | source [find target/stm32h7x.cfg] 5 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.thumbv7em-none-eabihf] 2 | runner = "arm-none-eabi-gdb -q -x openocd.gdb" 3 | rustflags = [ 4 | "-C", "link-arg=-Tlink.x" 5 | ] 6 | 7 | [build] 8 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 9 | -------------------------------------------------------------------------------- /swo.cfg: -------------------------------------------------------------------------------- 1 | # Use ST-Link interface (adjust for your setup if necessary) 2 | source [find interface/stlink.cfg] 3 | 4 | # STM32H7 target 5 | source [find target/stm32h7x.cfg] 6 | 7 | # Set the trace clock frequency (TRACECLKIN is the CPU clock, 400 MHz for STM32H755ZI-Q) 8 | # 2000000 baud rate works 9 | tpiu config internal /tmp/swo-output uart off 400000000 2000000 10 | 11 | # Enable ITM ports (port 0 for basic logging) 12 | itm ports on 13 | -------------------------------------------------------------------------------- /openocd.gdb: -------------------------------------------------------------------------------- 1 | target extended-remote :3333 2 | 3 | set print asm-demangle on 4 | set backtrace limit 8 5 | 6 | # Disable this for ITM support 7 | #monitor arm semihosting enable 8 | 9 | # Enable this for ITM support 10 | monitor tpiu config internal /tmp/itm.fifo uart off 480000000 2000000 11 | #monitor tpiu config external uart off 48000000 2000000 12 | monitor itm port 0 on 13 | 14 | break DefaultHandler 15 | break HardFault 16 | break rust_begin_unwind 17 | 18 | info mem 19 | load 20 | #stepi 21 | continue -------------------------------------------------------------------------------- /testsuite/Makefile: -------------------------------------------------------------------------------- 1 | # We need to pass the .cargo/config.toml info using environment 2 | # variables for sub-projects as cargo configs in parent directories 3 | # turn the local config into garbage. 4 | # 5 | # Possibly related: https://github.com/rust-lang/cargo/issues/7004 6 | 7 | TEST_FLAGS = CARGO_TARGET_THUMBV7EM_NONE_EABIHF_RUSTFLAGS="-C link-arg=-Tdefmt.x" \ 8 | CARGO_TARGET_THUMBV7EM_NONE_EABIHF_RUNNER="probe-run --chip STM32H745ZITx" \ 9 | CARGO_BUILD_TARGET="thumbv7em-none-eabihf" 10 | 11 | test: 12 | ${TEST_FLAGS} cargo test 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nucleo-h7xx 2 | 3 | Rust `no_std`, `embedded_hal` board support package for STMicroElectronics [STM32H7 Nucleo-144 boards](https://www.st.com/content/st_com/en/search.html#q=nucleo-h7-t=tools-page=1). 4 | 5 | 6 | # Board Support 7 | 8 | * [NUCLEO-H743ZI](https://www.st.com/en/evaluation-tools/nucleo-h743zi.html): Ordered, waiting for delivery. 9 | * [NUCLEO-H745ZI](https://www.st.com/en/evaluation-tools/nucleo-h745zi-q.html): Tested 10 | 11 | 12 | # Getting Started 13 | 14 | ``` 15 | cargo install cargo-generate 16 | 17 | cargo generate \ 18 | --git https://github.com/antoinevg/hello-nucleo-h7xx \ 19 | --name your-project-name 20 | ``` 21 | 22 | 23 | # Run Tests 24 | 25 | ``` 26 | cd testsuite 27 | make test 28 | ``` 29 | 30 | # Status 31 | 32 | Work in progress. 33 | -------------------------------------------------------------------------------- /examples/dsp/osc.rs: -------------------------------------------------------------------------------- 1 | use super::wavetable; 2 | 3 | 4 | // - wavetable oscillator ----------------------------------------------------- 5 | 6 | pub struct Wavetable { 7 | pub dx: f32, // frequency * (1 / fs) 8 | phasor: f32, 9 | shape: Shape, 10 | } 11 | 12 | pub enum Shape { 13 | Sin, 14 | Saw, 15 | } 16 | 17 | impl Wavetable { 18 | pub const fn new(shape: Shape) -> Wavetable { 19 | Wavetable { 20 | shape: shape, 21 | dx: 0.004_988_662_f32, // 220Hz @ fs=44100KHz 22 | phasor: 0., 23 | } 24 | } 25 | 26 | pub fn step(&mut self) -> f32 { 27 | let wt = match self.shape { 28 | Shape::Saw => &wavetable::SAW, 29 | Shape::Sin => &wavetable::SIN, 30 | }; 31 | 32 | let wt_index = self.phasor * wavetable::LENGTH as f32; 33 | let signal = wt[wt_index as usize]; 34 | self.phasor += self.dx; 35 | if self.phasor >= 1.0_f32 { 36 | self.phasor -= 1.0_f32; 37 | } 38 | signal 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/blinky_pac.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use cortex_m_rt::entry; 5 | use panic_semihosting as _; 6 | 7 | use nucleo_h7xx::hal::pac; 8 | 9 | #[entry] 10 | fn main() -> ! { 11 | // - configuration -------------------------------------------------------- 12 | 13 | let dp = pac::Peripherals::take().unwrap(); 14 | 15 | // enable gpioe peripheral clock - pac 16 | let rcc = &dp.RCC; 17 | rcc.ahb4enr.modify(|_, w| w.gpioeen().set_bit()); 18 | 19 | // configure user led pin 20 | let gpioe = &dp.GPIOE; 21 | gpioe.moder.modify(|_, w| w.moder1().output()); 22 | gpioe.otyper.modify(|_, w| w.ot1().push_pull()); 23 | gpioe.pupdr.modify(|_, w| w.pupdr1().pull_up()); 24 | gpioe.ospeedr.modify(|_, w| w.ospeedr1().high_speed()); 25 | 26 | // - main loop ------------------------------------------------------------ 27 | 28 | loop { 29 | gpioe.odr.modify(|_, w| w.odr1().high()); 30 | cortex_m::asm::delay(32_000_000); 31 | 32 | gpioe.odr.modify(|_, w| w.odr1().low()); 33 | cortex_m::asm::delay(32_000_000); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Antoine van Gelder 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | //! Board support crate for STMicroElectronics STM32H7 Nucleo-144 boards 4 | //! 5 | //! # Usage - see examples/ 6 | //! 7 | 8 | #![no_std] 9 | 10 | // - modules ------------------------------------------------------------------ 11 | 12 | pub mod board; 13 | pub mod clocks; 14 | #[cfg(any(feature = "ethernet"))] 15 | pub mod ethernet; 16 | pub mod led; 17 | pub mod pins; 18 | pub mod timer; 19 | 20 | #[cfg(any(feature = "log-itm"))] 21 | pub mod itm; 22 | 23 | // - log macros --------------------------------------------------------------- 24 | 25 | #[macro_export] 26 | macro_rules! loggit { 27 | ($($arg:tt)*) => ( 28 | #[cfg(feature = "log-itm")] 29 | { 30 | let itm = unsafe { &mut *cortex_m::peripheral::ITM::PTR }; 31 | cortex_m::iprintln!(&mut itm.stim[0], $($arg)*); 32 | } 33 | #[cfg(feature = "log-semihosting")] 34 | cortex_m_semihosting::hprintln!($($arg)*); 35 | ) 36 | } 37 | 38 | // - exports ------------------------------------------------------------------ 39 | 40 | pub use hal::hal as embedded_hal; 41 | pub use hal::pac; 42 | pub use stm32h7xx_hal as hal; 43 | 44 | pub use board::Board; 45 | -------------------------------------------------------------------------------- /examples/blinky_hal.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use panic_semihosting as _; 5 | 6 | use hal::rcc::PllConfigStrategy; 7 | use hal::{pac, prelude::*}; 8 | use nucleo_h7xx::hal; 9 | 10 | #[cortex_m_rt::entry] 11 | fn main() -> ! { 12 | let dp = pac::Peripherals::take().unwrap(); 13 | 14 | // - power & clocks ------------------------------------------------------- 15 | 16 | let pwr = dp.PWR.constrain(); 17 | let pwrcfg = pwr.smps().vos0(&dp.SYSCFG).freeze(); 18 | let ccdr = dp 19 | .RCC 20 | .constrain() 21 | .pll1_strategy(PllConfigStrategy::Iterative) // pll1 drives system clock 22 | .sys_ck(480.MHz()) // system clock @ 480 MHz 23 | .freeze(pwrcfg, &dp.SYSCFG); 24 | 25 | // - pins ----------------------------------------------------------------- 26 | 27 | let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB); 28 | let mut led_user = gpiob.pb14.into_push_pull_output(); 29 | led_user.set_low(); 30 | 31 | // - main loop ------------------------------------------------------------ 32 | 33 | loop { 34 | loop { 35 | led_user.toggle(); 36 | cortex_m::asm::delay(480_000_000); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /testsuite/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Antoine van Gelder "] 3 | name = "testsuite" 4 | publish = false 5 | edition = "2018" 6 | version = "0.1.0" 7 | 8 | [lib] 9 | name = "testsuite" 10 | test = false 11 | 12 | [[test]] 13 | name = "unit" 14 | harness = false 15 | 16 | [dependencies] 17 | nucleo-h7xx = { path = ".." } 18 | cortex-m = "0.7.5" 19 | cortex-m-rt = { version = "0.7.1" } 20 | defmt = "0.3.2" 21 | defmt-rtt = "0.3.2" 22 | defmt-test = "0.3.0" 23 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } 24 | 25 | [features] 26 | # set logging levels here 27 | default = [ 28 | # in tests, enable all logs 29 | "defmt-trace", 30 | # "dependency-a/defmt-trace", 31 | ] 32 | 33 | # do NOT modify these features 34 | defmt-default = [] 35 | defmt-trace = [] 36 | defmt-debug = [] 37 | defmt-info = [] 38 | defmt-warn = [] 39 | defmt-error = [] 40 | 41 | 42 | # - profiles ------------------------------------------------------------------ 43 | 44 | [profile.dev] 45 | codegen-units = 1 46 | debug = 2 47 | debug-assertions = true # <- 48 | incremental = false 49 | opt-level = 3 # <- 50 | overflow-checks = true # <- 51 | 52 | [profile.release] 53 | codegen-units = 1 54 | debug = 2 55 | debug-assertions = false # <- 56 | incremental = false 57 | lto = 'fat' 58 | opt-level = 3 # <- 59 | overflow-checks = false # <- 60 | -------------------------------------------------------------------------------- /examples/blinky.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use cortex_m_rt::entry; 5 | use panic_semihosting as _; 6 | 7 | use nucleo::hal::prelude::*; 8 | use nucleo::led::Led; 9 | use nucleo_h7xx as nucleo; 10 | 11 | #[entry] 12 | fn main() -> ! { 13 | // - board setup ---------------------------------------------------------- 14 | 15 | let board = nucleo::Board::take().unwrap(); 16 | 17 | let dp = nucleo::pac::Peripherals::take().unwrap(); 18 | 19 | let ccdr = board.freeze_clocks(dp.PWR.constrain(), dp.RCC.constrain(), &dp.SYSCFG); 20 | 21 | let pins = board.split_gpios( 22 | dp.GPIOA.split(ccdr.peripheral.GPIOA), 23 | dp.GPIOB.split(ccdr.peripheral.GPIOB), 24 | dp.GPIOC.split(ccdr.peripheral.GPIOC), 25 | dp.GPIOD.split(ccdr.peripheral.GPIOD), 26 | dp.GPIOE.split(ccdr.peripheral.GPIOE), 27 | dp.GPIOF.split(ccdr.peripheral.GPIOF), 28 | dp.GPIOG.split(ccdr.peripheral.GPIOG), 29 | ); 30 | 31 | let mut user_leds = nucleo::led::UserLeds::new(pins.user_leds); 32 | 33 | // - main loop ------------------------------------------------------------ 34 | 35 | let one_second = ccdr.clocks.sys_ck().raw(); 36 | 37 | loop { 38 | user_leds.ld3.off(); 39 | user_leds.ld1.on(); 40 | cortex_m::asm::delay(one_second); 41 | 42 | user_leds.ld1.off(); 43 | user_leds.ld2.on(); 44 | cortex_m::asm::delay(one_second); 45 | 46 | user_leds.ld2.off(); 47 | user_leds.ld3.on(); 48 | cortex_m::asm::delay(one_second); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | 10 | 11 | ## [0.3.0] - 2024-08-22 12 | ### Fixed 13 | - Use `core::ptr::addr_of_mut!` for static variables instead of `&mut`. (tx for #3 @pazmank!) 14 | - Update to semihosting to new `cortex_m_log` crate layout. 15 | 16 | 17 | ## [0.2.1] - 2022-10-17 18 | ### Changed 19 | - Update `cortex-m` to `0.7.6` 20 | - Update `stm32h7xx-hal` to `0.13.0` 21 | - Update `heapless` to `0.7.16` 22 | - Update `panic-semihosting` to `0.6.0` 23 | 24 | 25 | ## [0.2.0] - 2022-07-02 26 | ### Fixed 27 | - Panic at start due to `cortex-m-rt = 0.7.x` memory layout changes. (Ref: [rust-embedded/cortex-m #426](https://github.com/rust-embedded/cortex-m/issues/426#issuecomment-1092384050)) 28 | ### Changed 29 | - Update `cortex-m` to `0.7.5` 30 | - Update `cortex-m-rt` to `0.7.1` 31 | - Update `cortex-m-semihosting` to `0.5.0` 32 | - Update `stm32h7xx-hal` to `0.12.2` 33 | - Update `smoltcp` to `0.8.1` 34 | - Update `defmt` to `0.3.2` 35 | - Update `defmt-rtt` to `0.3.2` 36 | - Update `defmt-test` to `0.3.0` 37 | - Update `panic-probe` to `0.3.0` 38 | 39 | 40 | ## [0.1.2] - 2021-12-01 41 | ### Added 42 | - New Board method which takes a user-provided function to configure CCDR: `freeze_clocks_with()` 43 | 44 | 45 | ## [0.1.1] - 2021-04-01 46 | ### Changed 47 | - Changed license to MIT 48 | 49 | 50 | ## [0.1.0] - 2021-03-30 51 | ### Added 52 | - Initial release 53 | - Supported features are: 54 | * Default clock configuration @ 480 MHz 55 | * User LED's 56 | * Ethernet 57 | 58 | 59 | [Unreleased]: https://github.com/antoinevg/nucleo-h7xx/compare/v0.3.0...HEAD 60 | [0.3.0]: https://github.com/antoinevg/nucleo-h7xx/compare/v0.2.1...v0.3.0 61 | [0.2.1]: https://github.com/antoinevg/nucleo-h7xx/compare/v0.2.0...v0.2.1 62 | [0.2.0]: https://github.com/antoinevg/nucleo-h7xx/compare/v0.1.2...v0.2.0 63 | [0.1.2]: https://github.com/antoinevg/nucleo-h7xx/compare/v0.1.1...v0.1.2 64 | [0.1.1]: https://github.com/antoinevg/nucleo-h7xx/compare/v0.1.0...v0.1.1 65 | [0.1.0]: https://github.com/antoinevg/nucleo-h7xx/releases/tag/v0.1.0 66 | -------------------------------------------------------------------------------- /memory.x: -------------------------------------------------------------------------------- 1 | /** 2 | * See: https://github.com/stm32-rs/stm32h7xx-hal/blob/master/memory.x 3 | * https://www.st.com/resource/en/reference_manual/dm00176879-stm32h745-755-and-stm32h747-757-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf 4 | */ 5 | 6 | MEMORY 7 | { 8 | SRAM1 : ORIGIN = 0x30000000, LENGTH = 128K /* 32-bit AHB bus matrix, D2 domain */ 9 | SRAM2 : ORIGIN = 0x30020000, LENGTH = 128K /* 32-bit AHB bus matrix, D2 domain */ 10 | SRAM3 (RW) : ORIGIN = 0x30040000, LENGTH = 32K /* 32-bit AHB bus matrix, D2 domain */ 11 | SRAM4 (RW) : ORIGIN = 0x38000000, LENGTH = 64K /* 32-bit AHB bus matrix, D3 domain */ 12 | BSRAM : ORIGIN = 0x38800000, LENGTH = 4K /* 32-bit AHB bus matrix, D3 domain */ 13 | AXISRAM (RWX) : ORIGIN = 0x24000000, LENGTH = 512K /* 64-bit AXI bus matrix, D1 domain */ 14 | DTCMRAM (RWX) : ORIGIN = 0x20000000, LENGTH = 128K /* 64-bit AXI bus matrix, D1 domain */ 15 | FLASH2 (RX) : ORIGIN = 0x08100000, LENGTH = 1M /* 64-bit AXI bus matrix, D1 domain */ 16 | FLASH1 (RX) : ORIGIN = 0x08000000, LENGTH = 1M /* 64-bit AXI bus matrix, D1 domain */ 17 | ITCMRAM (RWX) : ORIGIN = 0x00000000, LENGTH = 64K /* 64-bit AXI bus matrix, D1 domain */ 18 | } 19 | 20 | /* stm32h7xx-hal uses a PROVIDE that expects RAM and FLASH symbols to exist */ 21 | REGION_ALIAS(RAM, DTCMRAM); 22 | REGION_ALIAS(FLASH, FLASH1); 23 | 24 | /* The location of the stack can be overridden using the 25 | `_stack_start` symbol. Place the stack at the end of RAM */ 26 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); 27 | 28 | SECTIONS 29 | { 30 | .itcmram : ALIGN(4) { 31 | *(.itcmram .itcmram.*); 32 | . = ALIGN(4); 33 | } > ITCMRAM 34 | 35 | .dtcmram : ALIGN(4) { 36 | *(.dtcmram .dtcmram.*); 37 | . = ALIGN(4); 38 | } > DTCMRAM 39 | 40 | .axisram : ALIGN(8) { 41 | *(.axisram .axisram.*); 42 | . = ALIGN(8); 43 | } > AXISRAM 44 | 45 | /* The SRAM1 and SRAM2 section are commonly used as the stack and 46 | heap for the CM4 core in dualcore versions and should thus not 47 | be used in examples */ 48 | 49 | .sram3 (NOLOAD) : ALIGN(4) { 50 | *(.sram3 .sram3.*); 51 | . = ALIGN(4); 52 | } > SRAM3 53 | 54 | .sram4 (NOLOAD) : ALIGN(4) { 55 | *(.sram4 .sram4.*); 56 | . = ALIGN(4); 57 | } > SRAM4 58 | }; 59 | -------------------------------------------------------------------------------- /src/led.rs: -------------------------------------------------------------------------------- 1 | pub use stm32h7xx_hal as hal; 2 | 3 | use embedded_hal::digital::v2::OutputPin; 4 | use hal::hal as embedded_hal; 5 | 6 | use crate::pins::user_leds; 7 | 8 | // - traits ------------------------------------------------------------------- 9 | 10 | /// Generic LED 11 | pub trait Led { 12 | /// Turns the LED off 13 | fn off(&mut self); 14 | 15 | /// Turns the LED on 16 | fn on(&mut self); 17 | } 18 | 19 | // - UserLed ------------------------------------------------------------------ 20 | 21 | pub struct UserLed(PIN); 22 | 23 | impl Led for UserLed 24 | where 25 | PIN: OutputPin, 26 | { 27 | fn on(&mut self) { 28 | if let Ok(()) = self.0.set_high() {} 29 | } 30 | 31 | fn off(&mut self) { 32 | if let Ok(()) = self.0.set_low() {} 33 | } 34 | } 35 | 36 | // - UserLeds ----------------------------------------------------------------- 37 | 38 | pub struct UserLeds { 39 | pub ld1: UserLed, 40 | pub ld2: UserLed, 41 | pub ld3: UserLed, 42 | } 43 | 44 | impl UserLeds { 45 | pub fn new(pins: user_leds::Pins) -> Self { 46 | Self { 47 | ld1: UserLed(pins.ld1.into_push_pull_output()), 48 | ld2: UserLed(pins.ld2.into_push_pull_output()), 49 | ld3: UserLed(pins.ld3.into_push_pull_output()), 50 | } 51 | } 52 | } 53 | 54 | // - UserLedsGeneric ---------------------------------------------------------- 55 | 56 | pub struct UserLedsGeneric { 57 | pub ld1: UserLed, 58 | pub ld2: UserLed, 59 | pub ld3: UserLed, 60 | } 61 | 62 | impl UserLedsGeneric 63 | where 64 | LD1: OutputPin, 65 | LD2: OutputPin, 66 | LD3: OutputPin, 67 | { 68 | fn new(pin1: LD1, pin2: LD2, pin3: LD3) -> Self { 69 | Self { 70 | ld1: UserLed(pin1), 71 | ld2: UserLed(pin2), 72 | ld3: UserLed(pin3), 73 | } 74 | } 75 | 76 | pub fn new2(pins: user_leds::Pins) -> user_leds::Type { 77 | UserLedsGeneric::new( 78 | pins.ld1.into_push_pull_output(), 79 | pins.ld2.into_push_pull_output(), 80 | pins.ld3.into_push_pull_output(), 81 | ) 82 | } 83 | } 84 | 85 | pub fn new(pins: user_leds::Pins) -> user_leds::Type { 86 | UserLedsGeneric::new( 87 | pins.ld1.into_push_pull_output(), 88 | pins.ld2.into_push_pull_output(), 89 | pins.ld3.into_push_pull_output(), 90 | ) 91 | } 92 | -------------------------------------------------------------------------------- /testsuite/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use core::sync::atomic::{AtomicUsize, Ordering}; 4 | 5 | use panic_probe as _; // panic handler 6 | use defmt_rtt as _; // global logger 7 | use nucleo_h7xx as nucleo; // bsp 8 | use nucleo::hal; // hal 9 | 10 | use hal::prelude::*; 11 | 12 | 13 | // - panic handler ------------------------------------------------------------ 14 | 15 | #[defmt::panic_handler] 16 | fn panic() -> ! { 17 | cortex_m::asm::udf() 18 | } 19 | 20 | 21 | // - defmt::timestamp --------------------------------------------------------- 22 | 23 | static COUNT: AtomicUsize = AtomicUsize::new(0); 24 | defmt::timestamp!("{=usize}", { 25 | // NOTE(no-CAS) `timestamps` runs with interrupts disabled 26 | let n = COUNT.load(Ordering::Relaxed); 27 | COUNT.store(n + 1, Ordering::Relaxed); 28 | n 29 | }); 30 | 31 | 32 | // - shared state ------------------------------------------------------------- 33 | 34 | pub struct State { 35 | pub flag: bool, 36 | pub board: nucleo::Board, 37 | pub clocks: hal::rcc::CoreClocks, 38 | pub pins: nucleo::pins::Pins, 39 | } 40 | 41 | #[allow(unused_mut)] 42 | impl State { 43 | pub fn init() -> State { 44 | let mut board = defmt::unwrap!(nucleo::Board::take()); 45 | 46 | let mut board = self::init(board); 47 | 48 | let dp = defmt::unwrap!(nucleo::pac::Peripherals::take()); 49 | 50 | let ccdr = board.freeze_clocks(dp.PWR.constrain(), 51 | dp.RCC.constrain(), 52 | &dp.SYSCFG); 53 | 54 | let pins = board.split_gpios(dp.GPIOA.split(ccdr.peripheral.GPIOA), 55 | dp.GPIOB.split(ccdr.peripheral.GPIOB), 56 | dp.GPIOC.split(ccdr.peripheral.GPIOC), 57 | dp.GPIOD.split(ccdr.peripheral.GPIOD), 58 | dp.GPIOE.split(ccdr.peripheral.GPIOE), 59 | dp.GPIOF.split(ccdr.peripheral.GPIOF), 60 | dp.GPIOG.split(ccdr.peripheral.GPIOG)); 61 | 62 | State { 63 | flag: true, 64 | board: board, 65 | clocks: ccdr.clocks, 66 | pins: pins, 67 | } 68 | } 69 | } 70 | 71 | 72 | // - board initialization ----------------------------------------------------- 73 | 74 | #[allow(unused_mut)] 75 | pub fn init(mut board: nucleo::Board) -> nucleo::Board { 76 | defmt::debug!("initializing board"); 77 | board 78 | } 79 | -------------------------------------------------------------------------------- /src/itm.rs: -------------------------------------------------------------------------------- 1 | /// Adapted from: 2 | /// 3 | /// Register addresses: 4 | /// 5 | /// SWO base: 0x5C00_3000 6 | /// SWTF base: 0x5C00_4000 (SWO funnnel) 7 | /// DBGMCU base: 0x5C00_1000 8 | /// ITM base: 0xE000_0000 (Instrumentation Trace Macrocell) 9 | /// DCB base: 0xE000_EDF0 (Debug Control Block) 10 | 11 | pub unsafe fn enable_itm( 12 | dcb: &mut cortex_m::peripheral::DCB, 13 | dbgmcu: &stm32h7xx_hal::stm32::DBGMCU, 14 | itm: &mut cortex_m::peripheral::ITM, 15 | c_ck: u32, 16 | swo_frequency: u32, 17 | ) { 18 | // DCB_DEMCR: Set TRCENA. Enables DWT and ITM units (DEMCR = Debug Exception and Monitor Control) 19 | //*(0xE000_EDFC as *mut u32) |= 1 << 24; 20 | dcb.enable_trace(); 21 | 22 | // DBGMCU_CR: Ensure debug blocks are clocked before interacting with them (page 3350) 23 | // *(0x5C00_1004 as *mut u32) 24 | dbgmcu.cr.modify(|_, w| { 25 | w.d3dbgcken() 26 | .set_bit() // D3 domain debug clock enable 27 | .d1dbgcken() 28 | .set_bit() // D1 domain debug clock enable 29 | .traceclken() 30 | .set_bit() // Enable trace port clock, TRACECLK 31 | .dbgslpd1() 32 | .set_bit() // Automatic clock stop/power-down disabled 33 | }); 34 | 35 | // SWO_LAR: Unlock SWO (LAR = Lock Access Register) 36 | *(0x5c00_3fb0 as *mut u32) = 0xC5AC_CE55; 37 | 38 | // SWTF_LAR: Unlock SWTF 39 | *(0x5c00_4fb0 as *mut u32) = 0xC5AC_CE55; 40 | 41 | // SWO_CODR Register: Set SWO speed 42 | *(0x5c00_3010 as *mut _) = (c_ck / swo_frequency) - 1; 43 | 44 | // SWO_SPPR Register: 45 | // 1 = Manchester, 2 = NRZ 46 | *(0x5c00_30f0 as *mut _) = 0x2; 47 | 48 | // SWTF_CTRL Trace Funnel: Enable for CM7 49 | *(0x5c00_4000 as *mut u32) |= (0 << 1) | // ENS1 - Slave port S1 enabled (Cortex-M4) 50 | (1 << 0); // ENS0 - Slave port S0 enabled (Cortex-M7) 51 | 52 | // ITM_LAR: Unlock ITM 53 | // *(0xE000_0FB0 as *mut u32) = 0xC5AC_CE55; 54 | itm.lar.write(0xC5AC_CE55); 55 | 56 | // ITM_TER: Enable lower 8 stimulus ports (TER = Trace Enable Register) 57 | // *(0xE000_0E00 as *mut u32) 58 | itm.ter[0].write(1); 59 | 60 | // ITM_TCR: Enable ITM (TCR = Trace Control Register) 61 | // *(0xE000_0E80 as *mut u32) 62 | itm.tcr.write( 63 | (0b00_0001 << 16) | // TraceBusID 64 | (1 << 3) | // TXENA - hardware event packet forwarding enable (enable SWO output ?) 65 | (1 << 0), // ITMENA - enable the ITM 66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /examples/itm.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use cortex_m::iprintln; 5 | use cortex_m_rt::entry; 6 | 7 | use nucleo_h7xx as nucleo; 8 | 9 | use hal::prelude::*; 10 | use nucleo::hal; 11 | 12 | use nucleo::led::Led; 13 | use nucleo::loggit; 14 | 15 | use log::{debug, error, info}; 16 | mod utilities; 17 | 18 | /// Example that demonstrates Cortex-M ITM (Instrumentation Trace Macrocell) output. 19 | /// To read ITM output, install the [itm](https://docs.rs/itm/latest/itm/) tool: 20 | /// ```bash 21 | /// cargo install itm 22 | /// ``` 23 | /// - Load the binary to the board 24 | /// - In one terminal run: 25 | /// ```bash 26 | /// openocd -f swo.cfg 27 | /// ``` 28 | /// - In an other terminal run: 29 | /// ```bash 30 | /// itmdump -F -f /tmp/swo-output 31 | /// ``` 32 | #[entry] 33 | fn main() -> ! { 34 | // - board setup ---------------------------------------------------------- 35 | 36 | let board = nucleo::Board::take().unwrap(); 37 | 38 | let dp = nucleo::pac::Peripherals::take().unwrap(); 39 | 40 | let ccdr = board.freeze_clocks(dp.PWR.constrain(), dp.RCC.constrain(), &dp.SYSCFG); 41 | 42 | let pins = board.split_gpios( 43 | dp.GPIOA.split(ccdr.peripheral.GPIOA), 44 | dp.GPIOB.split(ccdr.peripheral.GPIOB), 45 | dp.GPIOC.split(ccdr.peripheral.GPIOC), 46 | dp.GPIOD.split(ccdr.peripheral.GPIOD), 47 | dp.GPIOE.split(ccdr.peripheral.GPIOE), 48 | dp.GPIOF.split(ccdr.peripheral.GPIOF), 49 | dp.GPIOG.split(ccdr.peripheral.GPIOG), 50 | ); 51 | 52 | // - logger setup --------------------------------------------------------- 53 | 54 | utilities::logger::init(); 55 | 56 | let itm = unsafe { &mut *cortex_m::peripheral::ITM::PTR }; 57 | iprintln!(&mut itm.stim[0], "Hello itm example!"); 58 | 59 | info!("Hello itm example!"); 60 | debug!("Hello itm example!"); 61 | error!("Hello itm example!"); 62 | 63 | // - leds ----------------------------------------------------------------- 64 | 65 | let mut user_leds = nucleo::led::UserLeds::new(pins.user_leds); 66 | 67 | // - main loop ------------------------------------------------------------ 68 | 69 | let one_second = ccdr.clocks.sys_ck().raw(); 70 | let mut counter = 0; 71 | 72 | loop { 73 | loggit!("{}: one", counter); 74 | user_leds.ld3.off(); 75 | user_leds.ld1.on(); 76 | cortex_m::asm::delay(one_second); 77 | 78 | loggit!("{}: two", counter); 79 | user_leds.ld1.off(); 80 | user_leds.ld2.on(); 81 | cortex_m::asm::delay(one_second); 82 | 83 | loggit!("{}: three", counter); 84 | user_leds.ld2.off(); 85 | user_leds.ld3.on(); 86 | cortex_m::asm::delay(one_second); 87 | 88 | counter += 1; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /examples/utilities/logger.rs: -------------------------------------------------------------------------------- 1 | cfg_if::cfg_if! { 2 | if #[cfg(any(feature = "log-semihosting"))] { 3 | use panic_semihosting as _; 4 | 5 | use lazy_static::lazy_static; 6 | use log::LevelFilter; 7 | 8 | pub use cortex_m_log::log::Logger; 9 | use cortex_m_log::printer::semihosting; 10 | use cortex_m_log::printer::semihosting::Semihosting; 11 | use cortex_m_log::modes::InterruptOk; 12 | use cortex_m_log::printer::semihosting::hio::HStdout; 13 | 14 | lazy_static! { 15 | static ref LOGGER: Logger> = Logger { 16 | level: LevelFilter::Debug, 17 | inner: semihosting::InterruptOk::<_>::stdout().expect("Get Semihosting stdout"), 18 | }; 19 | } 20 | 21 | pub fn init() { 22 | cortex_m_log::log::init(&LOGGER).unwrap(); 23 | } 24 | } 25 | else if #[cfg(any(feature = "log-itm"))] { 26 | use panic_itm as _; 27 | 28 | use lazy_static::lazy_static; 29 | use log::LevelFilter; 30 | 31 | pub use cortex_m_log::log::Logger; 32 | 33 | use cortex_m_log::{ 34 | destination::Itm as ItmDest, 35 | printer::itm::InterruptSync, 36 | modes::InterruptFree, 37 | printer::itm::ItmSync 38 | }; 39 | 40 | lazy_static! { 41 | static ref LOGGER: Logger> = Logger { 42 | level: LevelFilter::Debug, 43 | inner: unsafe { 44 | InterruptSync::new( 45 | ItmDest::new(cortex_m::Peripherals::steal().ITM) 46 | ) 47 | }, 48 | }; 49 | } 50 | 51 | pub fn init() { 52 | cortex_m_log::log::init(&LOGGER).unwrap(); 53 | } 54 | 55 | } 56 | else if #[cfg(any(feature = "log-rtt"))] { 57 | use panic_rtt_target as _; 58 | 59 | use log::{Level, Metadata, Record, LevelFilter}; 60 | use rtt_target::{rprintln, rtt_init_print}; 61 | 62 | pub struct Logger { 63 | level: Level, 64 | } 65 | 66 | static LOGGER: Logger = Logger { 67 | level: Level::Debug, 68 | }; 69 | 70 | pub fn init() { 71 | rtt_init_print!(); 72 | log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Debug)).unwrap(); 73 | } 74 | 75 | impl log::Log for Logger { 76 | fn enabled(&self, metadata: &Metadata) -> bool { 77 | metadata.level() <= self.level 78 | 79 | } 80 | 81 | fn log(&self, record: &Record) { 82 | rprintln!("{} - {}", record.level(), record.args()); 83 | } 84 | 85 | fn flush(&self) {} 86 | } 87 | } 88 | else { 89 | use panic_halt as _; 90 | pub fn init() {} 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /testsuite/tests/unit.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | 5 | #[defmt_test::tests] 6 | mod tests { 7 | use defmt::{assert, assert_eq}; 8 | 9 | #[init] 10 | fn init() -> testsuite::State { 11 | testsuite::State::init() 12 | } 13 | 14 | #[test] 15 | fn assert_true() { 16 | assert!(true) 17 | } 18 | 19 | #[test] 20 | fn assert_eq() { 21 | assert_eq!(true, true, "TODO: write actual tests") 22 | } 23 | 24 | #[test] 25 | fn assert_state(state: &mut testsuite::State) { 26 | assert!(state.flag); 27 | state.flag = false; 28 | } 29 | 30 | #[test] 31 | fn assert_state_changed(state: &mut testsuite::State) { 32 | assert_eq!(state.flag, false); 33 | } 34 | 35 | #[test] 36 | fn assert_board_clocks(state: &mut testsuite::State) { 37 | let clocks = state.clocks; 38 | 39 | assert_eq!(clocks.hclk().raw(), 240_000_000, "AHB1,2,3"); 40 | assert_eq!(clocks.aclk().raw(), 240_000_000, "AXI"); 41 | assert_eq!(clocks.pclk1().raw(), 120_000_000, "APB1"); 42 | assert_eq!(clocks.ppre1(), 2, "APB1"); 43 | assert_eq!(clocks.pclk2().raw(), 120_000_000, "APB2"); 44 | assert_eq!(clocks.ppre2(), 2, "APB2"); 45 | assert_eq!(clocks.pclk3().raw(), 120_000_000, "APB3"); 46 | assert_eq!(clocks.ppre3(), 2, "APB3"); 47 | assert_eq!(clocks.pclk4().raw(), 120_000_000, "APB4"); 48 | assert_eq!(clocks.ppre4(), 2, "APB4"); 49 | 50 | assert_eq!(defmt::unwrap!(clocks.csi_ck()).raw(), 4_000_000); 51 | assert_eq!(defmt::unwrap!(clocks.hsi_ck()).raw(), 64_000_000); 52 | assert_eq!(defmt::unwrap!(clocks.hsi48_ck()).raw(), 48_000_000); 53 | assert_eq!(defmt::unwrap!(clocks.per_ck()).raw(), 64_000_000); 54 | assert!(clocks.hse_ck().is_none()); 55 | assert_eq!(defmt::unwrap!(clocks.lsi_ck()).raw(), 32_000); 56 | assert!(clocks.mco1_ck().is_none()); 57 | assert!(clocks.mco2_ck().is_none()); 58 | assert_eq!(defmt::unwrap!(clocks.pll1_p_ck()).raw(), 480_000_000); 59 | assert!(clocks.pll1_q_ck().is_none()); 60 | assert_eq!(defmt::unwrap!(clocks.pll1_r_ck()).raw(), 240_000_000); 61 | assert!(clocks.pll2_p_ck().is_none()); 62 | assert!(clocks.pll2_q_ck().is_none()); 63 | assert!(clocks.pll2_r_ck().is_none()); 64 | assert_eq!(defmt::unwrap!(clocks.pll3_p_ck()).raw(), 12_235_294); 65 | assert!(clocks.pll3_q_ck().is_none()); 66 | assert!(clocks.pll3_r_ck().is_none()); 67 | 68 | assert_eq!(clocks.sys_ck().raw(), 480_000_000, "SCGU"); 69 | assert_eq!(clocks.sysclk().raw(), clocks.sys_ck().raw()); // alias for sys_ck 70 | assert_eq!(clocks.timx_ker_ck().raw(), 240_000_000, "APB1"); 71 | assert_eq!(clocks.timy_ker_ck().raw(), 240_000_000, "APB2"); 72 | assert_eq!(clocks.c_ck().raw(), 480_000_000, "Core"); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /examples/button.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use core::cell::RefCell; 5 | use cortex_m::interrupt::Mutex; 6 | 7 | use cortex_m_rt::entry; 8 | use panic_semihosting as _; 9 | 10 | use nucleo::led::Led; 11 | use nucleo_h7xx as nucleo; 12 | 13 | use hal::gpio::ExtiPin; 14 | use hal::interrupt; 15 | use hal::prelude::*; 16 | use nucleo::hal; 17 | 18 | use nucleo::pac; 19 | 20 | // - global static state ------------------------------------------------------ 21 | 22 | static USER_BUTTON: Mutex>>> = 23 | Mutex::new(RefCell::new(None)); 24 | static USER_LEDS: Mutex>> = Mutex::new(RefCell::new(None)); 25 | 26 | // - entry-point -------------------------------------------------------------- 27 | 28 | #[entry] 29 | fn main() -> ! { 30 | // - board setup ---------------------------------------------------------- 31 | 32 | let board = nucleo::Board::take().unwrap(); 33 | 34 | let mut cp = cortex_m::Peripherals::take().unwrap(); 35 | let mut dp = pac::Peripherals::take().unwrap(); 36 | 37 | let ccdr = board.freeze_clocks(dp.PWR.constrain(), dp.RCC.constrain(), &dp.SYSCFG); 38 | 39 | let pins = board.split_gpios( 40 | dp.GPIOA.split(ccdr.peripheral.GPIOA), 41 | dp.GPIOB.split(ccdr.peripheral.GPIOB), 42 | dp.GPIOC.split(ccdr.peripheral.GPIOC), 43 | dp.GPIOD.split(ccdr.peripheral.GPIOD), 44 | dp.GPIOE.split(ccdr.peripheral.GPIOE), 45 | dp.GPIOF.split(ccdr.peripheral.GPIOF), 46 | dp.GPIOG.split(ccdr.peripheral.GPIOG), 47 | ); 48 | 49 | let mut user_button = pins.user_button.into_floating_input(); 50 | let user_leds = nucleo::led::UserLeds::new(pins.user_leds); 51 | 52 | // - configure interrupts ------------------------------------------------- 53 | 54 | user_button.make_interrupt_source(&mut dp.SYSCFG); 55 | user_button.trigger_on_edge(&mut dp.EXTI, hal::gpio::Edge::RisingFalling); 56 | user_button.enable_interrupt(&mut dp.EXTI); 57 | 58 | cortex_m::interrupt::free(|cs| { 59 | USER_BUTTON.borrow(cs).replace(Some(user_button)); 60 | USER_LEDS.borrow(cs).replace(Some(user_leds)); 61 | }); 62 | 63 | unsafe { 64 | cp.NVIC.set_priority(interrupt::EXTI15_10, 1); 65 | cortex_m::peripheral::NVIC::unmask(interrupt::EXTI15_10); 66 | } 67 | 68 | // - main loop ------------------------------------------------------------ 69 | 70 | loop { 71 | cortex_m::asm::wfi(); 72 | } 73 | } 74 | 75 | #[interrupt] 76 | fn EXTI15_10() { 77 | cortex_m::interrupt::free(|cs| { 78 | if let (Some(user_button), Some(user_leds)) = ( 79 | USER_BUTTON.borrow(cs).borrow_mut().as_mut(), 80 | USER_LEDS.borrow(cs).borrow_mut().as_mut(), 81 | ) { 82 | user_button.clear_interrupt_pending_bit(); 83 | 84 | if user_button.is_high() { 85 | user_leds.ld1.on(); 86 | user_leds.ld2.on(); 87 | user_leds.ld3.on(); 88 | } else { 89 | user_leds.ld1.off(); 90 | user_leds.ld2.off(); 91 | user_leds.ld3.off(); 92 | } 93 | } 94 | }); 95 | } 96 | -------------------------------------------------------------------------------- /src/timer.rs: -------------------------------------------------------------------------------- 1 | //! The stm32h7xx-hal implementation of the CountDown trait uses Hertz 2 | //! for the Time type with the result that it's duration is capped to 3 | //! 1000ms. 4 | //! 5 | //! This alternate implementation uses Duration instead, allowing for 6 | //! much longer periods. 7 | 8 | use hal::hal as embedded_hal; 9 | use hal::pac::{ 10 | TIM1, TIM12, TIM13, TIM14, TIM15, TIM16, TIM17, TIM2, TIM3, TIM4, TIM5, TIM6, TIM7, TIM8, 11 | }; 12 | use stm32h7xx_hal as hal; 13 | 14 | // - CountDownTimer ----------------------------------------------------------- 15 | 16 | pub struct CountDownTimer { 17 | timer: hal::timer::Timer, 18 | } 19 | 20 | impl CountDownTimer { 21 | pub fn new(timer: hal::timer::Timer) -> Self { 22 | Self { timer: timer } 23 | } 24 | } 25 | 26 | // - embedded_hal::timer::CountDown ------------------------------------------- 27 | 28 | macro_rules! impl_countdown { 29 | ($($TIMX:ident: ($cntType:ty),)+) => { 30 | $( 31 | impl embedded_hal::timer::Periodic for CountDownTimer<$TIMX> {} 32 | 33 | impl embedded_hal::timer::CountDown for CountDownTimer<$TIMX> { 34 | type Time = core::time::Duration; 35 | 36 | // TODO check for invalid values by using `cntType` 37 | fn start(&mut self, timeout: T) 38 | where 39 | T: Into, 40 | { 41 | // Pause 42 | self.timer.pause(); 43 | 44 | // Reset counter 45 | self.timer.reset_counter(); 46 | 47 | // UEV event occours on next overflow 48 | self.timer.urs_counter_only(); 49 | self.timer.clear_irq(); 50 | 51 | // Set PSC and ARR 52 | self.timer.set_timeout(timeout); 53 | 54 | // Generate an update event to force an update of the ARR register. This ensures 55 | // the first timer cycle is of the specified duration. 56 | self.timer.apply_freq(); 57 | 58 | // Start counter 59 | self.timer.resume() 60 | } 61 | 62 | fn wait(&mut self) -> nb::Result<(), void::Void> { 63 | if self.timer.is_irq_clear() { 64 | Err(nb::Error::WouldBlock) 65 | } else { 66 | self.timer.clear_irq(); 67 | Ok(()) 68 | } 69 | } 70 | } 71 | )+ 72 | } 73 | } 74 | 75 | impl_countdown! { 76 | // Advanced-control 77 | TIM1: (u16), 78 | TIM8: (u16), 79 | 80 | // General-purpose 81 | TIM2: (u32), 82 | TIM3: (u16), 83 | TIM4: (u16), 84 | TIM5: (u32), 85 | 86 | // Basic 87 | TIM6: (u16), 88 | TIM7: (u16), 89 | 90 | // General-purpose 91 | TIM12: (u16), 92 | TIM13: (u16), 93 | TIM14: (u16), 94 | 95 | // General-purpose 96 | TIM15: (u16), 97 | TIM16: (u16), 98 | TIM17: (u16), 99 | } 100 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nucleo-h7xx" 3 | version = "0.3.0" 4 | authors = [ "Antoine van Gelder " ] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "Board support crate for the STMicroelectronics STM32H7 Nucleo-144 boards." 8 | repository = "https://github.com/antoinevg/nucleo-h7xx" 9 | documentation = "https://github.com/antoinevg/nucleo-h7xx/wiki" 10 | categories = [ 11 | "embedded", 12 | "hardware-support", 13 | "no-std", 14 | ] 15 | keywords = [ 16 | "cortex-m", 17 | "bsp", 18 | "stm32", 19 | "stm32h7xx", 20 | "nucleo", 21 | ] 22 | readme = "README.md" 23 | exclude = [ 24 | "testsuite/*", 25 | "openocd.cfg", 26 | "openocd.gdb", 27 | ] 28 | 29 | [package.metadata.docs.rs] 30 | features = [] 31 | targets = [ "thumbv7em-none-eabihf" ] 32 | 33 | 34 | # - workspace ----------------------------------------------------------------- 35 | 36 | # TODO Workspace feature is blocked: https://github.com/rust-lang/cargo/issues/7004 37 | #[workspace] 38 | #members = [ 39 | # "testsuite" 40 | #] 41 | 42 | 43 | # - features ------------------------------------------------------------------ 44 | 45 | [features] 46 | default = [] 47 | ethernet = [ "stm32h7xx-hal/ethernet", "smoltcp" ] 48 | defmt = [ "stm32h7xx-hal/defmt" ] 49 | button-1-pa0 = [] # SB81=on, SB82=off 50 | led-1-pa5 = [] # SB65=on, SB54=off 51 | log-semihosting = [ "cortex-m-semihosting" ] 52 | log-itm = [] 53 | log-rtt = [] 54 | 55 | 56 | # - dependencies -------------------------------------------------------------- 57 | 58 | [dependencies] 59 | cortex-m = "0.7.6" 60 | cortex-m-rt = { version = "0.7.1" } 61 | stm32h7xx-hal = { version = "0.13.0", features = [ "stm32h747cm7" ] } 62 | 63 | embedded-timeout-macros = "0.3.0" 64 | heapless = "0.7.16" 65 | nb = "1.0.0" 66 | void = { version = "1.0.2", default-features = false } 67 | 68 | # - optional dependencies 69 | 70 | smoltcp = { version = "0.8.1", default-features = false, features = [ 71 | "medium-ethernet", 72 | "proto-ipv4", 73 | "proto-ipv6", 74 | "socket-raw", 75 | "socket-udp" 76 | ], optional = true } 77 | cortex-m-semihosting = { version = "0.5.0", optional = true } 78 | 79 | 80 | # - dev dependencies ---------------------------------------------------------- 81 | 82 | [dev-dependencies] 83 | cfg-if = "1.0.0" 84 | cortex-m-log = { version = "0.7.0", features = [ "itm", "semihosting", "log-integration" ] } 85 | lazy_static = { version = "1.4.0", features = [ "spin_no_std" ] } 86 | log = "0.4.14" 87 | panic-halt = "0.2.0" 88 | panic-itm = { version = "0.4.2" } 89 | panic-rtt-target = { version = "0.1.1", features = [ "cortex-m" ] } 90 | panic-semihosting = { version = "0.6.0" } 91 | rtt-target = { version = "0.3.1", features = [ "cortex-m" ] } 92 | 93 | 94 | # - profiles ------------------------------------------------------------------ 95 | 96 | [profile.dev] 97 | 98 | [profile.release] 99 | debug = true 100 | 101 | 102 | # - examples ------------------------------------------------------------------ 103 | 104 | [[example]] 105 | name = "blinky" 106 | required-features = [] 107 | 108 | [[example]] 109 | name = "blinky_hal" 110 | required-features = [] 111 | 112 | [[example]] 113 | name = "blinky_pac" 114 | required-features = [] 115 | 116 | [[example]] 117 | name = "ethernet" 118 | required-features = [ "ethernet" ] 119 | 120 | [[example]] 121 | name = "ethernet_hal" 122 | required-features = [ "ethernet", "log-semihosting" ] 123 | 124 | [[example]] 125 | name = "itm" 126 | required-features = [ "log-itm" ] 127 | -------------------------------------------------------------------------------- /src/board.rs: -------------------------------------------------------------------------------- 1 | use stm32h7xx_hal as hal; 2 | 3 | use crate::clocks; 4 | use crate::led; 5 | use crate::pins; 6 | 7 | // - global static state ------------------------------------------------------ 8 | 9 | // `no_mangle` is used here to prevent linking different minor 10 | // versions of this crate as that would let you `take` the core 11 | // peripherals more than once (one per minor version) 12 | #[no_mangle] 13 | static NUCLEO_H7XX_BOARD: () = (); 14 | 15 | /// Set to `true` when `take` was called to make `Board` a singleton. 16 | static mut TAKEN: bool = false; 17 | 18 | // - Board -------------------------------------------------------------------- 19 | 20 | pub struct Board; 21 | 22 | impl Board { 23 | #[inline] 24 | pub fn take() -> Option { 25 | cortex_m::interrupt::free(|_| { 26 | if unsafe { TAKEN } { 27 | None 28 | } else { 29 | Some(unsafe { Board::steal() }) 30 | } 31 | }) 32 | } 33 | 34 | #[inline] 35 | pub unsafe fn steal() -> Self { 36 | Board 37 | } 38 | 39 | pub fn freeze_clocks( 40 | &self, 41 | pwr: hal::pwr::Pwr, 42 | rcc: hal::rcc::Rcc, 43 | syscfg: &hal::device::SYSCFG, 44 | ) -> hal::rcc::Ccdr { 45 | clocks::configure(pwr, rcc, syscfg) 46 | } 47 | 48 | pub fn freeze_clocks_with( 49 | &self, 50 | pwr: hal::pwr::Pwr, 51 | rcc: hal::rcc::Rcc, 52 | syscfg: &hal::device::SYSCFG, 53 | configure: fn( 54 | pwrcfg: hal::pwr::PowerConfiguration, 55 | rcc: hal::rcc::Rcc, 56 | syscfg: &hal::device::SYSCFG, 57 | ) -> hal::rcc::Ccdr, 58 | ) -> hal::rcc::Ccdr { 59 | clocks::configure_with(pwr, rcc, syscfg, configure) 60 | } 61 | 62 | /// Takes the board's GPIO peripherals and split them into ZST's 63 | /// representing the individual GPIO pins used by the board. 64 | pub fn split_gpios( 65 | &self, 66 | gpioa: hal::gpio::gpioa::Parts, 67 | gpiob: hal::gpio::gpiob::Parts, 68 | gpioc: hal::gpio::gpioc::Parts, 69 | gpiod: hal::gpio::gpiod::Parts, 70 | gpioe: hal::gpio::gpioe::Parts, 71 | gpiof: hal::gpio::gpiof::Parts, 72 | gpiog: hal::gpio::gpiog::Parts, 73 | ) -> pins::Pins { 74 | pins::Pins::new(gpioa, gpiob, gpioc, gpiod, gpioe, gpiof, gpiog) 75 | } 76 | 77 | pub fn split_led_user(&self, pins: pins::user_leds::Pins) -> led::UserLeds { 78 | led::UserLeds::new(pins) 79 | } 80 | } 81 | 82 | // - macros ------------------------------------------------------------------- 83 | 84 | #[macro_export] 85 | macro_rules! board_freeze_clocks { 86 | ($board:expr, $dp:expr) => {{ 87 | use nucleo_h7xx::hal::prelude::_stm32h7xx_hal_pwr_PwrExt; 88 | use nucleo_h7xx::hal::prelude::_stm32h7xx_hal_rcc_RccExt; 89 | $board.freeze_clocks($dp.PWR.constrain(), $dp.RCC.constrain(), &$dp.SYSCFG) 90 | }}; 91 | } 92 | 93 | #[macro_export] 94 | macro_rules! board_split_gpios { 95 | ($board:expr, $ccdr:expr, $dp:expr) => {{ 96 | use nucleo_h7xx::hal::gpio::GpioExt; 97 | $board.split_gpios( 98 | $dp.GPIOA.split($ccdr.peripheral.GPIOA), 99 | $dp.GPIOB.split($ccdr.peripheral.GPIOB), 100 | $dp.GPIOC.split($ccdr.peripheral.GPIOC), 101 | $dp.GPIOD.split($ccdr.peripheral.GPIOD), 102 | $dp.GPIOE.split($ccdr.peripheral.GPIOE), 103 | $dp.GPIOF.split($ccdr.peripheral.GPIOF), 104 | $dp.GPIOG.split($ccdr.peripheral.GPIOG), 105 | ) 106 | }}; 107 | } 108 | -------------------------------------------------------------------------------- /examples/ethernet.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_imports)] 3 | #![allow(unused_parens)] 4 | #![allow(unused_variables)] 5 | #![no_main] 6 | #![no_std] 7 | 8 | use cortex_m; 9 | use cortex_m_rt::entry; 10 | 11 | use nucleo::loggit; 12 | use nucleo_h7xx as nucleo; 13 | 14 | use hal::gpio::Speed::*; 15 | use hal::hal::digital::v2::OutputPin; 16 | use hal::hal::digital::v2::ToggleableOutputPin; 17 | use hal::prelude::*; 18 | use hal::rcc::CoreClocks; 19 | use hal::{ethernet, ethernet::PHY}; 20 | use nucleo::hal; 21 | 22 | use hal::pac; 23 | use pac::interrupt; 24 | 25 | use smoltcp; 26 | use smoltcp::iface::{Interface, InterfaceBuilder, Neighbor, NeighborCache, Route, Routes}; 27 | use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; 28 | use smoltcp::storage::PacketMetadata; 29 | use smoltcp::time::Instant; 30 | use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, IpEndpoint, Ipv4Address, Ipv6Cidr}; 31 | 32 | use log::{debug, error, info}; 33 | 34 | /// Simple ethernet example that will respond to icmp pings on 35 | /// `IP_LOCAL` and periodically send a udp packet to 36 | /// `IP_REMOTE:IP_REMOTE_PORT` 37 | /// 38 | /// You can start a simple listening server with netcat: 39 | /// 40 | /// nc -u -l 34254 41 | 42 | const MAC_LOCAL: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; 43 | const IP_LOCAL: [u8; 4] = [192, 168, 20, 99]; 44 | const IP_REMOTE: [u8; 4] = [192, 168, 20, 207]; 45 | const IP_REMOTE_PORT: u16 = 34254; 46 | 47 | mod utilities; 48 | 49 | #[entry] 50 | fn main() -> ! { 51 | // - endpoints ------------------------------------------------------------ 52 | 53 | let local_endpoint = IpEndpoint::new(Ipv4Address::from_bytes(&IP_LOCAL).into(), 1234); 54 | let remote_endpoint = 55 | IpEndpoint::new(Ipv4Address::from_bytes(&IP_REMOTE).into(), IP_REMOTE_PORT); 56 | 57 | // - board setup ---------------------------------------------------------- 58 | 59 | info!("Setting up board"); 60 | 61 | let board = nucleo::Board::take().unwrap(); 62 | 63 | let dp = pac::Peripherals::take().unwrap(); 64 | 65 | let ccdr = board.freeze_clocks(dp.PWR.constrain(), dp.RCC.constrain(), &dp.SYSCFG); 66 | 67 | let pins = board.split_gpios( 68 | dp.GPIOA.split(ccdr.peripheral.GPIOA), 69 | dp.GPIOB.split(ccdr.peripheral.GPIOB), 70 | dp.GPIOC.split(ccdr.peripheral.GPIOC), 71 | dp.GPIOD.split(ccdr.peripheral.GPIOD), 72 | dp.GPIOE.split(ccdr.peripheral.GPIOE), 73 | dp.GPIOF.split(ccdr.peripheral.GPIOF), 74 | dp.GPIOG.split(ccdr.peripheral.GPIOG), 75 | ); 76 | 77 | utilities::logger::init(); 78 | 79 | // - ethernet interface --------------------------------------------------- 80 | 81 | info!("Bringing up ethernet interface"); 82 | 83 | let timeout_timer = dp 84 | .TIM17 85 | .timer(100.Hz(), ccdr.peripheral.TIM17, &ccdr.clocks); 86 | let timeout_timer = nucleo::timer::CountDownTimer::new(timeout_timer); 87 | let timeout_timer = match nucleo::ethernet::EthernetInterface::start( 88 | pins.ethernet, 89 | &MAC_LOCAL, 90 | &IP_LOCAL, 91 | ccdr.peripheral.ETH1MAC, 92 | &ccdr.clocks, 93 | timeout_timer, 94 | ) { 95 | Ok(tim17) => tim17, 96 | Err(e) => { 97 | error!("Failed to start ethernet interface: {:?}", e); 98 | loop {} 99 | } 100 | }; 101 | 102 | // wait for link to come up 103 | info!("Waiting for link to come up"); 104 | nucleo::ethernet::EthernetInterface::interrupt_free( 105 | |ethernet_interface| { 106 | while !ethernet_interface.poll_link() {} 107 | }, 108 | ); 109 | 110 | // create and bind socket 111 | let socket_handle = nucleo::ethernet::EthernetInterface::interrupt_free(|ethernet_interface| { 112 | let socket_handle = ethernet_interface.new_udp_socket(); 113 | let socket = ethernet_interface 114 | .interface 115 | .as_mut() 116 | .unwrap() 117 | .get_socket::(socket_handle); 118 | match socket.bind(local_endpoint) { 119 | Ok(()) => return socket_handle, 120 | Err(e) => { 121 | error!("Failed to bind socket to endpoint: {:?}", local_endpoint); 122 | loop {} 123 | } 124 | } 125 | }); 126 | 127 | // - main loop ------------------------------------------------------------ 128 | 129 | info!("Entering main loop"); 130 | 131 | let mut last = 0; 132 | 133 | loop { 134 | cortex_m::asm::wfi(); 135 | 136 | // poll ethernet interface 137 | let now = nucleo::ethernet::EthernetInterface::interrupt_free(|ethernet_interface| { 138 | match ethernet_interface.poll() { 139 | Ok(result) => {} // packets were processed or emitted 140 | Err(smoltcp::Error::Exhausted) => (), 141 | Err(smoltcp::Error::Unrecognized) => (), 142 | Err(e) => debug!("ethernet::EthernetInterface.poll() -> {:?}", e), 143 | } 144 | ethernet_interface.now() 145 | }); 146 | 147 | // check if it has been 5 seconds since we last sent something 148 | if (now - last) < 5000 { 149 | continue; 150 | } else { 151 | last = now; 152 | } 153 | 154 | // send something 155 | nucleo::ethernet::EthernetInterface::interrupt_free(|ethernet_interface| { 156 | let socket = ethernet_interface 157 | .interface 158 | .as_mut() 159 | .unwrap() 160 | .get_socket::(socket_handle); 161 | match socket.send_slice("nucleo says hello!\n".as_bytes(), remote_endpoint) { 162 | Ok(()) => (), 163 | Err(smoltcp::Error::Exhausted) => (), 164 | Err(e) => error!("UdpSocket::send error: {:?}", e), 165 | }; 166 | }); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/clocks.rs: -------------------------------------------------------------------------------- 1 | use crate::hal; 2 | use hal::pac; 3 | use hal::prelude::*; 4 | use hal::pwr; 5 | use hal::rcc; 6 | use hal::time::Hertz; 7 | use hal::time::MegaHertz; 8 | 9 | // - constants ---------------------------------------------------------------- 10 | 11 | // SAI clock uses pll3 12 | const PLL3_P: Hertz = Hertz::Hz(48_000 * 256); 13 | 14 | // - types -------------------------------------------------------------------- 15 | 16 | pub trait HseCrystal { 17 | // by default it uses the MCO output of the ST-Link which has a fixed frequency of 8 MHz 18 | // TODO Add a feature to support the onboard HSE oscillator from X2, a 25 MHz crystal. 19 | const CRYSTAL_FREQ: MegaHertz = MegaHertz::MHz(8); 20 | fn use_hse_crystal(self) -> Self; 21 | } 22 | 23 | impl HseCrystal for rcc::Rcc { 24 | fn use_hse_crystal(self) -> Self { 25 | self.use_hse(Self::CRYSTAL_FREQ.convert()) 26 | } 27 | } 28 | 29 | // - configure ---------------------------------------------------------------- 30 | 31 | /// Configures system clocks: 32 | /// 33 | /// HSE crystal 34 | /// 480 MHz system clock 35 | /// PLL3 for SAI audio 36 | /// 37 | /// Usage: 38 | /// 39 | /// ``` 40 | /// let dp = pac::Peripherals::take().unwrap(); 41 | /// let ccdr = configure(dp.PWR.constrain(), 42 | /// dp.RCC.constrain(), 43 | /// &dp.SYSCFG); 44 | /// ``` 45 | pub fn configure(pwr: pwr::Pwr, rcc: rcc::Rcc, syscfg: &pac::SYSCFG) -> rcc::Ccdr { 46 | #[cfg(not(feature = "log-itm"))] 47 | let ccdr = configure_with(pwr, rcc, syscfg, |pwrcfg, rcc, syscfg| { 48 | rcc.sys_ck(480.MHz()) // system clock @ 480 MHz 49 | .pll1_strategy(rcc::PllConfigStrategy::Iterative) // pll1 drives system clock 50 | .pll3_p_ck(PLL3_P) // sai clock @ 12.288 MHz 51 | //.use_hse_crystal() // TODO hse oscillator @ 25 MHz 52 | .freeze(pwrcfg, syscfg) 53 | }); 54 | 55 | #[cfg(any(feature = "log-itm"))] 56 | let ccdr = configure_with(pwr, rcc, syscfg, |pwrcfg, rcc, syscfg| { 57 | rcc.sys_ck(480.MHz()) // system clock @ 480 MHz 58 | .pll1_strategy(rcc::PllConfigStrategy::Iterative) // pll1 drives system clock 59 | .pll1_r_ck(480.MHz()) // TRACECLK 60 | .pll3_p_ck(PLL3_P) // sai clock @ 12.288 MHz 61 | //.use_hse_crystal() // TODO hse oscillator @ 25 MHz 62 | .freeze(pwrcfg, syscfg) 63 | }); 64 | 65 | ccdr 66 | } 67 | 68 | /// Configure system CCDR using a provided function 69 | pub fn configure_with( 70 | pwr: hal::pwr::Pwr, 71 | rcc: hal::rcc::Rcc, 72 | syscfg: &hal::device::SYSCFG, 73 | function: fn( 74 | pwrcfg: hal::pwr::PowerConfiguration, 75 | rcc: hal::rcc::Rcc, 76 | syscfg: &hal::device::SYSCFG, 77 | ) -> hal::rcc::Ccdr, 78 | ) -> hal::rcc::Ccdr { 79 | let mut cp = unsafe { cortex_m::Peripherals::steal() }; 80 | let dp = unsafe { pac::Peripherals::steal() }; 81 | 82 | // link SRAM3 power state to CPU1 83 | dp.RCC.ahb2enr.modify(|_, w| w.sram3en().set_bit()); 84 | 85 | // configure ccdr using provided function 86 | let pwrcfg = pwr.smps().vos0(syscfg).freeze(); 87 | let ccdr = function(pwrcfg, rcc, syscfg); 88 | 89 | // enable itm if the feature is selected 90 | #[cfg(any(feature = "log-itm"))] 91 | unsafe { 92 | let swo_frequency = 2_000_000; 93 | crate::itm::enable_itm( 94 | &mut cp.DCB, 95 | &dp.DBGMCU, 96 | &mut cp.ITM, 97 | ccdr.clocks.c_ck().raw(), 98 | swo_frequency, 99 | ); 100 | } 101 | 102 | // configure cpu 103 | cp.SCB.invalidate_icache(); 104 | cp.SCB.enable_icache(); 105 | cp.DWT.enable_cycle_counter(); 106 | 107 | ccdr 108 | } 109 | 110 | #[cfg(any(feature = "log-itm", feature = "log-semihosting"))] 111 | pub fn log_clocks(clocks: &hal::rcc::CoreClocks) { 112 | use crate::loggit; 113 | 114 | loggit!("AHB1,2,3,4 hclk: {}", clocks.hclk()); 115 | loggit!("AXI aclk: {}", clocks.aclk()); 116 | loggit!("APB1 pclk1: {}", clocks.pclk1()); 117 | loggit!("APB1 ppre1: {}", clocks.ppre1()); 118 | loggit!("APB2 pclk2: {}", clocks.pclk2()); 119 | loggit!("APB2 ppre2: {}", clocks.ppre2()); 120 | loggit!("APB3 pclk3: {}", clocks.pclk3()); 121 | loggit!("APB3 ppre3: {}", clocks.ppre3()); 122 | loggit!("APB4 pclk4: {}", clocks.pclk4()); 123 | loggit!("APB4 ppre4: {}", clocks.ppre4()); 124 | 125 | loggit!("csi_ck: {}", clocks.csi_ck().unwrap_or_else(|| 0.Hz())); 126 | loggit!("hsi_ck: {}", clocks.hsi_ck().unwrap_or_else(|| 0.Hz())); 127 | loggit!("hsi48_ck: {}", clocks.hsi48_ck().unwrap_or_else(|| 0.Hz())); 128 | loggit!("per_ck: {}", clocks.per_ck().unwrap_or_else(|| 0.Hz())); 129 | loggit!("hse_ck: {}", clocks.hse_ck().unwrap_or_else(|| 0.Hz())); 130 | loggit!("lsi_ck: {}", clocks.lsi_ck().unwrap_or_else(|| 0.Hz())); 131 | loggit!("mco1_ck: {}", clocks.mco1_ck().unwrap_or_else(|| 0.Hz())); 132 | loggit!("mco2_ck: {}", clocks.mco2_ck().unwrap_or_else(|| 0.Hz())); 133 | loggit!( 134 | "pll1_p_ck: {}", 135 | clocks.pll1_p_ck().unwrap_or_else(|| 0.Hz()) 136 | ); 137 | loggit!( 138 | "pll1_q_ck: {}", 139 | clocks.pll1_q_ck().unwrap_or_else(|| 0.Hz()) 140 | ); 141 | loggit!( 142 | "pll1_r_ck: {}", 143 | clocks.pll1_r_ck().unwrap_or_else(|| 0.Hz()) 144 | ); 145 | loggit!( 146 | "pll2_p_ck: {}", 147 | clocks.pll2_p_ck().unwrap_or_else(|| 0.Hz()) 148 | ); 149 | loggit!( 150 | "pll2_q_ck: {}", 151 | clocks.pll2_q_ck().unwrap_or_else(|| 0.Hz()) 152 | ); 153 | loggit!( 154 | "pll2_r_ck: {}", 155 | clocks.pll2_r_ck().unwrap_or_else(|| 0.Hz()) 156 | ); 157 | loggit!( 158 | "pll3_p_ck: {}", 159 | clocks.pll3_p_ck().unwrap_or_else(|| 0.Hz()) 160 | ); 161 | loggit!( 162 | "pll3_q_ck: {}", 163 | clocks.pll3_q_ck().unwrap_or_else(|| 0.Hz()) 164 | ); 165 | loggit!( 166 | "pll3_r_ck: {}", 167 | clocks.pll3_r_ck().unwrap_or_else(|| 0.Hz()) 168 | ); 169 | 170 | loggit!("SCGU sys_ck: {}", clocks.sys_ck()); 171 | loggit!("SCGU sysclk: {}", clocks.sysclk()); 172 | loggit!("APB1 timx_ker_ck: {}", clocks.timx_ker_ck()); 173 | loggit!("APB2 timy_ker_ck: {}", clocks.timy_ker_ck()); 174 | loggit!("Core c_ck: {}", clocks.c_ck()); 175 | } 176 | -------------------------------------------------------------------------------- /examples/ethernet_hal.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use cortex_m_semihosting::hprintln; 5 | /// Simple ethernet example that will respond to icmp pings on 6 | /// `IP_LOCAL` and periodically send a udp packet to 7 | /// `IP_REMOTE:IP_REMOTE_PORT` 8 | /// 9 | /// Also see: https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/ethernet-rtic-stm32h747i-disco.rs 10 | /// https://github.com/adamgreig/stm32f4-smoltcp-demo 11 | use panic_semihosting as _; 12 | 13 | use core::sync::atomic::{AtomicU32, Ordering}; 14 | 15 | use cortex_m; 16 | use cortex_m_rt::{entry, exception}; 17 | 18 | use hal::rcc::CoreClocks; 19 | use hal::{ethernet, ethernet::PHY}; 20 | use stm32h7xx_hal as hal; 21 | 22 | use hal::{pac, prelude::*}; 23 | use pac::interrupt; 24 | 25 | use smoltcp; 26 | use smoltcp::iface::{ 27 | Interface, InterfaceBuilder, Neighbor, NeighborCache, Route, Routes, SocketStorage, 28 | }; 29 | use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; 30 | use smoltcp::storage::PacketMetadata; 31 | use smoltcp::time::Instant; 32 | use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, IpEndpoint, Ipv4Address, Ipv6Cidr}; 33 | 34 | // - global constants --------------------------------------------------------- 35 | 36 | const MAC_LOCAL: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; 37 | const IP_LOCAL: [u8; 4] = [192, 168, 20, 99]; 38 | const IP_REMOTE: [u8; 4] = [192, 168, 20, 207]; 39 | const IP_REMOTE_PORT: u16 = 34254; 40 | 41 | const MAX_UDP_PACKET_SIZE: usize = 576; 42 | 43 | // - global static state ------------------------------------------------------ 44 | 45 | static ATOMIC_TIME: AtomicU32 = AtomicU32::new(0); 46 | 47 | static mut ETHERNET: Option = None; 48 | static mut ETHERNET_STORAGE: EthernetStorage = EthernetStorage::new(); 49 | 50 | #[link_section = ".sram3.eth"] 51 | static mut ETHERNET_DESCRIPTOR_RING: ethernet::DesRing<4, 4> = ethernet::DesRing::new(); 52 | 53 | // - entry points ------------------------------------------------------------- 54 | 55 | #[entry] 56 | fn main() -> ! { 57 | let dp = pac::Peripherals::take().unwrap(); 58 | let mut cp = pac::CorePeripherals::take().unwrap(); 59 | 60 | // - power & clocks ------------------------------------------------------- 61 | 62 | let pwr = dp.PWR.constrain(); 63 | let pwrcfg = pwr.smps().freeze(); 64 | 65 | // link SRAM3 power state to CPU1 66 | dp.RCC.ahb2enr.modify(|_, w| w.sram3en().set_bit()); 67 | 68 | let rcc = dp.RCC.constrain(); 69 | let ccdr = rcc 70 | .sys_ck(200.MHz()) 71 | .hclk(200.MHz()) 72 | .pll1_r_ck(100.MHz()) // for TRACECK 73 | .freeze(pwrcfg, &dp.SYSCFG); 74 | 75 | cp.SCB.invalidate_icache(); 76 | cp.SCB.enable_icache(); 77 | cp.DWT.enable_cycle_counter(); 78 | 79 | // - leds ----------------------------------------------------------------- 80 | 81 | let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB); 82 | let gpioe = dp.GPIOE.split(ccdr.peripheral.GPIOE); 83 | 84 | let mut led_user = gpiob.pb14.into_push_pull_output(); // LED3, red 85 | let mut led_link = gpioe.pe1.into_push_pull_output(); // LED2, yellow 86 | led_user.set_high(); 87 | led_link.set_low(); 88 | 89 | // - ethernet ------------------------------------------------------------- 90 | 91 | use hal::gpio::gpioa; 92 | use hal::gpio::gpiob; 93 | use hal::gpio::gpioc; 94 | use hal::gpio::gpiog; 95 | type AlternateFunction11 = hal::gpio::Alternate<11>; 96 | 97 | let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); 98 | let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC); 99 | let gpiog = dp.GPIOG.split(ccdr.peripheral.GPIOG); 100 | 101 | let _rmii_ref_clk: gpioa::PA1 = gpioa.pa1.into_alternate(); 102 | let _rmii_mdio: gpioa::PA2 = gpioa.pa2.into_alternate(); 103 | let _rmii_mdc: gpioc::PC1 = gpioc.pc1.into_alternate(); 104 | let _rmii_crs_dv: gpioa::PA7 = gpioa.pa7.into_alternate(); 105 | let _rmii_rxd0: gpioc::PC4 = gpioc.pc4.into_alternate(); 106 | let _rmii_rxd1: gpioc::PC5 = gpioc.pc5.into_alternate(); 107 | let _rmii_tx_en: gpiog::PG11 = gpiog.pg11.into_alternate(); 108 | let _rmii_txd0: gpiog::PG13 = gpiog.pg13.into_alternate(); 109 | let _rmii_txd1: gpiob::PB13 = gpiob.pb13.into_alternate(); 110 | 111 | assert_eq!(ccdr.clocks.hclk().raw(), 200_000_000); // HCLK 200MHz 112 | assert_eq!(ccdr.clocks.pclk1().raw(), 100_000_000); // PCLK 100MHz 113 | assert_eq!(ccdr.clocks.pclk2().raw(), 100_000_000); // PCLK 100MHz 114 | assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz 115 | 116 | let mac_addr = EthernetAddress::from_bytes(&MAC_LOCAL); 117 | let (eth_dma, eth_mac) = unsafe { 118 | ethernet::new_unchecked( 119 | dp.ETHERNET_MAC, 120 | dp.ETHERNET_MTL, 121 | dp.ETHERNET_DMA, 122 | &mut *core::ptr::addr_of_mut!(ETHERNET_DESCRIPTOR_RING), 123 | mac_addr.clone(), 124 | ccdr.peripheral.ETH1MAC, 125 | &ccdr.clocks, 126 | ) 127 | }; 128 | 129 | // initialise PHY and wait for link to come up 130 | let mut lan8742a = ethernet::phy::LAN8742A::new(eth_mac.set_phy_addr(0)); 131 | lan8742a.phy_reset(); 132 | lan8742a.phy_init(); 133 | while !lan8742a.poll_link() {} 134 | 135 | // enable ethernet interrupt 136 | unsafe { 137 | ethernet::enable_interrupt(); 138 | cp.NVIC.set_priority(pac::Interrupt::ETH, 196); // mid prio 139 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::ETH); 140 | } 141 | 142 | // create global static ETHERNET object 143 | unsafe { 144 | ETHERNET = Some(Net::new(&mut *core::ptr::addr_of_mut!(ETHERNET_STORAGE), eth_dma, mac_addr)); 145 | } 146 | 147 | // - udp socket ----------------------------------------------------------- 148 | 149 | let store = unsafe { &mut *core::ptr::addr_of_mut!(ETHERNET_STORAGE) }; 150 | let udp_rx_buffer = UdpSocketBuffer::new( 151 | &mut store.udp_rx_metadata[..], 152 | &mut store.udp_rx_buffer_storage[..], 153 | ); 154 | let udp_tx_buffer = UdpSocketBuffer::new( 155 | &mut store.udp_tx_metadata[..], 156 | &mut store.udp_tx_buffer_storage[..], 157 | ); 158 | let mut udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); 159 | 160 | let endpoint_local = IpEndpoint::new(Ipv4Address::from_bytes(&IP_LOCAL).into(), 12345); 161 | let endpoint_remote = 162 | IpEndpoint::new(Ipv4Address::from_bytes(&IP_REMOTE).into(), IP_REMOTE_PORT); 163 | if !udp_socket.is_open() { 164 | udp_socket.bind(endpoint_local).unwrap(); 165 | } 166 | 167 | let udp_socket_handle = unsafe { ETHERNET.as_mut().unwrap().interface.add_socket(udp_socket) }; 168 | 169 | // - timer ---------------------------------------------------------------- 170 | 171 | systick_init(&mut cp.SYST, &ccdr.clocks); // 1ms tick 172 | let mut delay = cp.SYST.delay(ccdr.clocks); 173 | 174 | // - main loop ------------------------------------------------------------ 175 | 176 | led_user.set_low(); 177 | 178 | loop { 179 | match lan8742a.poll_link() { 180 | true => led_link.set_high(), 181 | _ => led_link.set_low(), 182 | } 183 | 184 | // send a packet 185 | let udp_socket = unsafe { 186 | ETHERNET 187 | .as_mut() 188 | .unwrap() 189 | .interface 190 | .get_socket::(udp_socket_handle) 191 | }; 192 | match udp_socket.send_slice("hello there\n".as_bytes(), endpoint_remote) { 193 | Ok(()) => (), 194 | Err(e) => hprintln!("oops: {:?}", e), 195 | } 196 | 197 | delay.delay_ms(2000_u16); 198 | } 199 | } 200 | 201 | // - systick ------------------------------------------------------------------ 202 | 203 | fn systick_init(syst: &mut pac::SYST, clocks: &CoreClocks) { 204 | let c_ck_mhz = clocks.c_ck().raw() / 1_000_000; 205 | let syst_calib = 0x3E8; 206 | syst.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core); 207 | syst.set_reload((syst_calib * c_ck_mhz) - 1); 208 | syst.enable_interrupt(); 209 | syst.enable_counter(); 210 | } 211 | 212 | // - interrupts and exceptions ------------------------------------------------ 213 | 214 | #[interrupt] 215 | fn ETH() { 216 | unsafe { ethernet::interrupt_handler() }; 217 | 218 | if let Some(ethernet) = unsafe { ETHERNET.as_mut() } { 219 | let time = ATOMIC_TIME.load(Ordering::Relaxed); 220 | ethernet.poll(time as i64); 221 | } 222 | } 223 | 224 | #[exception] 225 | fn SysTick() { 226 | ATOMIC_TIME.fetch_add(1, Ordering::Relaxed); 227 | } 228 | 229 | // - NetStaticStorage --------------------------------------------------------- 230 | 231 | pub struct EthernetStorage<'a> { 232 | ip_addrs: [IpCidr; 1], 233 | socket_storage: [SocketStorage<'a>; 8], 234 | neighbor_cache_storage: [Option<(IpAddress, Neighbor)>; 8], 235 | routes_storage: [Option<(IpCidr, Route)>; 1], 236 | 237 | // network buffers 238 | udp_rx_metadata: [PacketMetadata; 1], 239 | udp_tx_metadata: [PacketMetadata; 1], 240 | udp_rx_buffer_storage: [u8; MAX_UDP_PACKET_SIZE], 241 | udp_tx_buffer_storage: [u8; MAX_UDP_PACKET_SIZE], 242 | } 243 | 244 | impl<'a> EthernetStorage<'a> { 245 | pub const fn new() -> Self { 246 | EthernetStorage { 247 | ip_addrs: [IpCidr::Ipv6(Ipv6Cidr::SOLICITED_NODE_PREFIX)], 248 | socket_storage: [SocketStorage::EMPTY; 8], 249 | neighbor_cache_storage: [None; 8], 250 | routes_storage: [None; 1], 251 | 252 | udp_rx_metadata: [UdpPacketMetadata::EMPTY], 253 | udp_tx_metadata: [UdpPacketMetadata::EMPTY], 254 | udp_rx_buffer_storage: [0u8; MAX_UDP_PACKET_SIZE], 255 | udp_tx_buffer_storage: [0u8; MAX_UDP_PACKET_SIZE], 256 | } 257 | } 258 | } 259 | 260 | // - Net ---------------------------------------------------------------------- 261 | 262 | pub struct Net<'a> { 263 | interface: Interface<'a, ethernet::EthernetDMA<'a, 4, 4>>, 264 | } 265 | 266 | impl<'a> Net<'a> { 267 | pub fn new( 268 | store: &'static mut EthernetStorage<'a>, 269 | ethdev: ethernet::EthernetDMA<'a, 4, 4>, 270 | ethernet_addr: EthernetAddress, 271 | ) -> Self { 272 | store.ip_addrs = [IpCidr::new(Ipv4Address::from_bytes(&IP_LOCAL).into(), 0)]; 273 | 274 | let neighbor_cache = NeighborCache::new(&mut store.neighbor_cache_storage[..]); 275 | let routes = Routes::new(&mut store.routes_storage[..]); 276 | let interface = InterfaceBuilder::new(ethdev, &mut store.socket_storage[..]) 277 | .hardware_addr(ethernet_addr.into()) 278 | .neighbor_cache(neighbor_cache) 279 | .ip_addrs(&mut store.ip_addrs[..]) 280 | .routes(routes) 281 | .finalize(); 282 | 283 | Net { interface } 284 | } 285 | 286 | // poll ethernet interface 287 | pub fn poll(&mut self, now: i64) { 288 | let timestamp = Instant::from_millis(now); 289 | 290 | match self.interface.poll(timestamp) { 291 | Ok(_) => (), 292 | Err(smoltcp::Error::Exhausted) => (), 293 | Err(smoltcp::Error::Unrecognized) => (), 294 | Err(e) => hprintln!("Error polling: {:?}", e), 295 | }; 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/pins.rs: -------------------------------------------------------------------------------- 1 | pub use stm32h7xx_hal as hal; 2 | 3 | // - types -------------------------------------------------------------------- 4 | 5 | pub type A0 = hal::gpio::gpioa::PA3; 6 | pub type A1 = hal::gpio::gpioc::PC0; 7 | pub type A2 = hal::gpio::gpioc::PC3; 8 | pub type A3 = hal::gpio::gpiob::PB1; 9 | pub type A4 = hal::gpio::gpioc::PC2; 10 | pub type A5 = hal::gpio::gpiof::PF11; 11 | pub type A6 = hal::gpio::gpiof::PF6; 12 | pub type A7 = hal::gpio::gpiof::PF10; 13 | pub type A8 = hal::gpio::gpioa::PA2; 14 | 15 | pub type D0 = hal::gpio::gpiob::PB7; 16 | pub type D1 = hal::gpio::gpiob::PB6; 17 | pub type D2 = hal::gpio::gpiog::PG14; 18 | pub type D3 = hal::gpio::gpioe::PE13; 19 | pub type D4 = hal::gpio::gpioe::PE14; 20 | pub type D5 = hal::gpio::gpioe::PE11; 21 | pub type D6 = hal::gpio::gpioa::PA8; 22 | pub type D7 = hal::gpio::gpiog::PG12; 23 | pub type D8 = hal::gpio::gpiog::PG9; 24 | pub type D9 = hal::gpio::gpiod::PD15; 25 | 26 | pub type D10 = hal::gpio::gpiod::PD14; 27 | pub type D11 = hal::gpio::gpiob::PB5; 28 | pub type D12 = hal::gpio::gpioa::PA6; 29 | pub type D13 = hal::gpio::gpioa::PA5; 30 | pub type D14 = hal::gpio::gpiob::PB9; 31 | pub type D15 = hal::gpio::gpiob::PB8; 32 | pub type D16 = hal::gpio::gpioc::PC6; 33 | pub type D17 = hal::gpio::gpiob::PB15; 34 | pub type D18 = hal::gpio::gpiob::PB13; 35 | pub type D19 = hal::gpio::gpiob::PB12; 36 | 37 | pub type D20 = hal::gpio::gpioa::PA15>; 38 | pub type D21 = hal::gpio::gpioc::PC7; 39 | pub type D22 = hal::gpio::gpiob::PB5; 40 | pub type D23 = hal::gpio::gpiob::PB3>; 41 | pub type D24 = hal::gpio::gpioa::PA4; 42 | pub type D25 = hal::gpio::gpiob::PB4>; 43 | pub type D26 = hal::gpio::gpiog::PG6; 44 | pub type D27 = hal::gpio::gpiob::PB2; 45 | pub type D28 = hal::gpio::gpiod::PD13; 46 | pub type D29 = hal::gpio::gpiod::PD12; 47 | 48 | pub type D30 = hal::gpio::gpiod::PD11; 49 | pub type D31 = hal::gpio::gpioe::PE2; 50 | pub type D32 = hal::gpio::gpioa::PA0; 51 | pub type D33 = hal::gpio::gpiob::PB0; 52 | pub type D34 = hal::gpio::gpioe::PE0; 53 | pub type D35 = hal::gpio::gpiob::PB11; 54 | pub type D36 = hal::gpio::gpiob::PB10; 55 | pub type D37 = hal::gpio::gpioe::PE15; 56 | pub type D38 = hal::gpio::gpioe::PE6; 57 | pub type D39 = hal::gpio::gpioe::PE12; 58 | 59 | pub type D40 = hal::gpio::gpioe::PE10; 60 | pub type D41 = hal::gpio::gpioe::PE7; 61 | pub type D42 = hal::gpio::gpioe::PE8; 62 | pub type D43 = hal::gpio::gpioc::PC8; 63 | pub type D44 = hal::gpio::gpioc::PC9; 64 | pub type D45 = hal::gpio::gpioc::PC10; 65 | pub type D46 = hal::gpio::gpioc::PC11; 66 | pub type D47 = hal::gpio::gpioc::PC12; 67 | pub type D48 = hal::gpio::gpiod::PD2; 68 | pub type D49 = hal::gpio::gpiog::PG10; 69 | 70 | pub type D50 = hal::gpio::gpiog::PG8; 71 | pub type D51 = hal::gpio::gpiod::PD7; 72 | pub type D52 = hal::gpio::gpiod::PD6; 73 | pub type D53 = hal::gpio::gpiod::PD5; 74 | pub type D54 = hal::gpio::gpiod::PD4; 75 | pub type D55 = hal::gpio::gpiod::PD3; 76 | pub type D56 = hal::gpio::gpioe::PE2; 77 | pub type D57 = hal::gpio::gpioe::PE4; 78 | pub type D58 = hal::gpio::gpioe::PE5; 79 | pub type D59 = hal::gpio::gpioe::PE6; 80 | 81 | pub type D60 = hal::gpio::gpioe::PE3; 82 | pub type D61 = hal::gpio::gpiof::PF8; 83 | pub type D62 = hal::gpio::gpiof::PF7; 84 | pub type D63 = hal::gpio::gpiof::PF9; 85 | pub type D64 = hal::gpio::gpiod::PD10; 86 | pub type D65 = hal::gpio::gpiob::PB14; 87 | pub type D66 = hal::gpio::gpiod::PD1; 88 | pub type D67 = hal::gpio::gpiod::PD0; 89 | pub type D68 = hal::gpio::gpiof::PF15; 90 | pub type D69 = hal::gpio::gpiof::PF14; 91 | 92 | pub type D70 = hal::gpio::gpiob::PB5; 93 | pub type D71 = hal::gpio::gpioe::PE9; 94 | pub type D72 = hal::gpio::gpiob::PB2; 95 | 96 | pub mod user_button { 97 | use stm32h7xx_hal as hal; 98 | 99 | #[cfg(not(feature = "button-1-pa0"))] // SB81=off, SB82=on 100 | pub type Pin = hal::gpio::gpioc::PC13; 101 | #[cfg(any(feature = "button-1-pa0"))] // SB81=on, SB82=off 102 | pub type Pin = hal::gpio::gpioa::PA0; 103 | } 104 | 105 | pub mod user_leds { 106 | use hal::gpio::{Output, PushPull}; 107 | use stm32h7xx_hal as hal; 108 | 109 | #[cfg(not(feature = "led-1-pa5"))] // SB65=off, SB54=on 110 | pub type Pin1 = hal::gpio::gpiob::PB0; 111 | #[cfg(any(feature = "led-1-pa5"))] // SB65=on, SB54=off 112 | pub type Pin1 = hal::gpio::gpioa::PA5; 113 | pub type Pin2 = hal::gpio::gpioe::PE1; 114 | pub type Pin3 = hal::gpio::gpiob::PB14; 115 | 116 | #[cfg(not(feature = "led-1-pa5"))] 117 | pub type Ld1 = hal::gpio::gpiob::PB0>; 118 | #[cfg(any(feature = "led-1-pa5"))] 119 | pub type Ld1 = hal::gpio::gpioa::PA5>; 120 | pub type Ld2 = hal::gpio::gpioe::PE1>; 121 | pub type Ld3 = hal::gpio::gpiob::PB14>; 122 | 123 | pub type Type = crate::led::UserLedsGeneric; 124 | 125 | pub struct Pins { 126 | pub ld1: Pin1, 127 | pub ld2: Pin2, 128 | pub ld3: Pin3, 129 | } 130 | } 131 | 132 | pub mod ethernet { 133 | use stm32h7xx_hal as hal; 134 | 135 | pub struct Pins { 136 | pub ref_clk: hal::gpio::gpioa::PA1, // REFCLK, // RmiiRefClk 137 | pub md_io: hal::gpio::gpioa::PA2, // IO, // MDIO 138 | pub md_clk: hal::gpio::gpioc::PC1, // CLK, // MDC 139 | pub crs: hal::gpio::gpioa::PA7, // CRS, // RmiiCrsDv 140 | pub rx_d0: hal::gpio::gpioc::PC4, // RXD0, // RmiiRxD0 141 | pub rx_d1: hal::gpio::gpioc::PC5, // RXD1, // RmiiRxD0 142 | pub tx_en: hal::gpio::gpiog::PG11, // TXEN, // RmiiTxEN 143 | pub tx_d0: hal::gpio::gpiog::PG13, // TXD0, // RmiiTxD0 144 | pub tx_d1: hal::gpio::gpiob::PB13, // TXD1, // RmiiTxD1 145 | } 146 | } 147 | 148 | // - Pins --------------------------------------------------------------------- 149 | 150 | pub struct Pins { 151 | // ST Zio "Arduino" pins 152 | pub a0: A0, 153 | pub a1: A1, 154 | pub a2: A2, 155 | pub a3: A3, 156 | pub a4: A4, 157 | pub a5: A5, 158 | pub a6: A6, 159 | pub a7: A7, 160 | //pub a8: A8, // used by ethernet (PA2) 161 | pub d0: D0, 162 | pub d1: D1, 163 | pub d2: D2, 164 | pub d3: D3, 165 | pub d4: D4, 166 | pub d5: D5, 167 | pub d6: D6, 168 | pub d7: D7, 169 | pub d8: D8, 170 | pub d9: D9, 171 | pub d10: D10, 172 | pub d11: D11, 173 | pub d12: D12, 174 | #[cfg(not(feature = "led-1-pa5"))] 175 | pub d13: D13, // used by ld1 alt. (PA5) 176 | pub d14: D14, 177 | pub d15: D15, 178 | pub d16: D16, 179 | pub d17: D17, 180 | //pub d18: D18, // used by ethernet (PB13) 181 | pub d19: D19, 182 | pub d20: D20, 183 | pub d21: D21, 184 | //pub d22: D22, // duplicated by d11 (PB5) 185 | pub d23: D23, 186 | pub d24: D24, 187 | pub d25: D25, 188 | pub d26: D26, 189 | pub d27: D27, 190 | pub d28: D28, 191 | pub d29: D29, 192 | pub d30: D30, 193 | pub d31: D31, 194 | #[cfg(not(feature = "button-1-pa0"))] 195 | pub d32: D32, // used by b1 (PA0) 196 | #[cfg(any(feature = "led-1-pa5"))] 197 | pub d33: D33, // used by ld1 (PB0) 198 | pub d34: D34, 199 | pub d35: D35, 200 | pub d36: D36, 201 | pub d37: D37, 202 | pub d38: D38, 203 | pub d39: D39, 204 | pub d40: D40, 205 | pub d41: D41, 206 | pub d42: D42, 207 | pub d43: D43, 208 | pub d44: D44, 209 | pub d45: D45, 210 | pub d46: D46, 211 | pub d47: D47, 212 | pub d48: D48, 213 | pub d49: D49, 214 | pub d50: D50, 215 | pub d51: D51, 216 | pub d52: D52, 217 | pub d53: D53, 218 | pub d54: D54, 219 | pub d55: D55, 220 | //pub d56: D56, // duplicated by d31 (PE2) 221 | pub d57: D57, 222 | pub d58: D58, 223 | //pub d59: D59, // duplicated by d38 (PE6) 224 | pub d60: D60, 225 | pub d61: D61, 226 | pub d62: D62, 227 | pub d63: D63, 228 | pub d64: D64, 229 | //pub d65: D65, // used by ld3 (PB14) 230 | pub d66: D66, 231 | pub d67: D67, 232 | pub d68: D68, 233 | pub d69: D69, 234 | //pub d70: D70, // duplicated by d11 (PB5) 235 | pub d71: D71, 236 | //pub d72: D72, // duplicated by d27 (PB2) 237 | 238 | // board peripherals 239 | pub ethernet: ethernet::Pins, 240 | pub user_button: user_button::Pin, 241 | pub user_leds: user_leds::Pins, 242 | // TODO 243 | } 244 | 245 | // - construction ------------------------------------------------------------- 246 | 247 | impl Pins { 248 | pub fn new( 249 | gpioa: hal::gpio::gpioa::Parts, 250 | gpiob: hal::gpio::gpiob::Parts, 251 | gpioc: hal::gpio::gpioc::Parts, 252 | gpiod: hal::gpio::gpiod::Parts, 253 | gpioe: hal::gpio::gpioe::Parts, 254 | gpiof: hal::gpio::gpiof::Parts, 255 | gpiog: hal::gpio::gpiog::Parts, 256 | ) -> Self { 257 | Self { 258 | a0: gpioa.pa3, 259 | a1: gpioc.pc0, 260 | a2: gpioc.pc3, 261 | a3: gpiob.pb1, 262 | a4: gpioc.pc2, 263 | a5: gpiof.pf11, 264 | a6: gpiof.pf6, 265 | a7: gpiof.pf10, 266 | //a8: gpioa.pa2, 267 | d0: gpiob.pb7, 268 | d1: gpiob.pb6, 269 | d2: gpiog.pg14, 270 | d3: gpioe.pe13, 271 | d4: gpioe.pe14, 272 | d5: gpioe.pe11, 273 | d6: gpioa.pa8, 274 | d7: gpiog.pg12, 275 | d8: gpiog.pg9, 276 | d9: gpiod.pd15, 277 | 278 | d10: gpiod.pd14, 279 | d11: gpiob.pb5, 280 | d12: gpioa.pa6, 281 | #[cfg(not(feature = "led-1-pa5"))] 282 | d13: gpioa.pa5, 283 | d14: gpiob.pb9, 284 | d15: gpiob.pb8, 285 | d16: gpioc.pc6, 286 | d17: gpiob.pb15, 287 | //d18: gpiob.pb13, 288 | d19: gpiob.pb12, 289 | 290 | d20: gpioa.pa15, 291 | d21: gpioc.pc7, 292 | //d22: gpiob.pb5, 293 | d23: gpiob.pb3, 294 | d24: gpioa.pa4, 295 | d25: gpiob.pb4, 296 | d26: gpiog.pg6, 297 | d27: gpiob.pb2, 298 | d28: gpiod.pd13, 299 | d29: gpiod.pd12, 300 | 301 | d30: gpiod.pd11, 302 | d31: gpioe.pe2, 303 | #[cfg(not(feature = "button-1-pa0"))] 304 | d32: gpioa.pa0, 305 | #[cfg(any(feature = "led-1-pa5"))] 306 | d33: gpiob.pb0, 307 | d34: gpioe.pe0, 308 | d35: gpiob.pb11, 309 | d36: gpiob.pb10, 310 | d37: gpioe.pe15, 311 | d38: gpioe.pe6, 312 | d39: gpioe.pe12, 313 | 314 | d40: gpioe.pe10, 315 | d41: gpioe.pe7, 316 | d42: gpioe.pe8, 317 | d43: gpioc.pc8, 318 | d44: gpioc.pc9, 319 | d45: gpioc.pc10, 320 | d46: gpioc.pc11, 321 | d47: gpioc.pc12, 322 | d48: gpiod.pd2, 323 | d49: gpiog.pg10, 324 | 325 | d50: gpiog.pg8, 326 | d51: gpiod.pd7, 327 | d52: gpiod.pd6, 328 | d53: gpiod.pd5, 329 | d54: gpiod.pd4, 330 | d55: gpiod.pd3, 331 | //d56: gpioe.pe2, 332 | d57: gpioe.pe4, 333 | d58: gpioe.pe5, 334 | //d59: gpioe.pe6, 335 | d60: gpioe.pe3, 336 | d61: gpiof.pf8, 337 | d62: gpiof.pf7, 338 | d63: gpiof.pf9, 339 | d64: gpiod.pd10, 340 | //d65: gpiob.pb14, 341 | d66: gpiod.pd1, 342 | d67: gpiod.pd0, 343 | d68: gpiof.pf15, 344 | d69: gpiof.pf14, 345 | 346 | //d70: gpiob.pb5, 347 | d71: gpioe.pe9, 348 | //d72: gpiob.pb2, 349 | ethernet: ethernet::Pins { 350 | ref_clk: gpioa.pa1, 351 | md_io: gpioa.pa2, 352 | md_clk: gpioc.pc1, 353 | crs: gpioa.pa7, 354 | rx_d0: gpioc.pc4, 355 | rx_d1: gpioc.pc5, 356 | tx_en: gpiog.pg11, 357 | tx_d0: gpiog.pg13, 358 | tx_d1: gpiob.pb13, 359 | }, 360 | 361 | #[cfg(not(feature = "button-1-pa0"))] 362 | user_button: gpioc.pc13, 363 | #[cfg(any(feature = "button-1-pa0"))] 364 | user_button: gpioa.pa0, 365 | 366 | user_leds: user_leds::Pins { 367 | #[cfg(not(feature = "led-1-pa5"))] 368 | ld1: gpiob.pb0, 369 | #[cfg(any(feature = "led-1-pa5"))] 370 | ld1: gpioa.pa5, 371 | ld2: gpioe.pe1, 372 | ld3: gpiob.pb14, 373 | }, 374 | } 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /src/ethernet.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_imports)] 3 | #![allow(unused_variables)] 4 | 5 | use core::cell::RefCell; 6 | use core::sync::atomic::{AtomicU32, Ordering}; 7 | 8 | use cortex_m::interrupt::Mutex; 9 | use cortex_m_rt::exception; 10 | 11 | use hal::gpio::Speed::*; 12 | use hal::hal::digital::v2::OutputPin; 13 | use hal::prelude::*; 14 | use hal::time::MilliSeconds; 15 | use hal::{ethernet, ethernet::PHY}; 16 | use stm32h7xx_hal as hal; 17 | 18 | use hal::pac; 19 | use pac::interrupt; 20 | 21 | use embedded_timeout_macros::{block_timeout, repeat_timeout, TimeoutError}; 22 | 23 | use smoltcp; 24 | use smoltcp::iface::{ 25 | Interface, InterfaceBuilder, Neighbor, NeighborCache, Route, Routes, SocketHandle, 26 | SocketStorage, 27 | }; 28 | use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; 29 | use smoltcp::storage::PacketMetadata; 30 | use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, IpEndpoint, Ipv4Address, Ipv6Cidr}; 31 | 32 | use heapless::Vec; 33 | 34 | use crate::pins; 35 | use crate::timer::CountDownTimer as Timer; 36 | 37 | // - global constants --------------------------------------------------------- 38 | 39 | pub const MAX_UDP_PACKET_SIZE: usize = 576; 40 | //pub const MAX_UDP_PACKET_SIZE: usize = 4096; 41 | 42 | // - global static state ------------------------------------------------------ 43 | 44 | #[link_section = ".sram3.eth"] 45 | pub static mut ETHERNET_DESCRIPTOR_RING: ethernet::DesRing<4, 4> = ethernet::DesRing::new(); 46 | 47 | static mut ETHERNET_MUTEX: Mutex>> = 48 | Mutex::new(RefCell::new(None)); 49 | pub static ATOMIC_TIME: AtomicU32 = AtomicU32::new(0); 50 | 51 | // - statically allocated storage --------------------------------------------- 52 | 53 | static mut ETHERNET_STORAGE: EthernetStorage = EthernetStorage::new(); 54 | static mut ETHERNET_SOCKETS_STORAGE: Vec = Vec::new(); 55 | 56 | pub struct EthernetStorage<'a> { 57 | ip_addrs: [IpCidr; 1], 58 | socket_storage: [SocketStorage<'a>; 8], 59 | neighbor_cache_storage: [Option<(IpAddress, Neighbor)>; 8], 60 | routes_storage: [Option<(IpCidr, Route)>; 1], 61 | } 62 | 63 | impl<'a> EthernetStorage<'a> { 64 | const fn new() -> Self { 65 | EthernetStorage { 66 | ip_addrs: [IpCidr::Ipv6(Ipv6Cidr::SOLICITED_NODE_PREFIX)], 67 | socket_storage: [SocketStorage::EMPTY; 8], 68 | neighbor_cache_storage: [None; 8], 69 | routes_storage: [None; 1], 70 | } 71 | } 72 | } 73 | 74 | #[derive(Debug)] 75 | pub struct UdpSocketStorage<'a> { 76 | socket_handle: Option, 77 | udp_rx_metadata: [PacketMetadata; 1], 78 | udp_tx_metadata: [PacketMetadata; 1], 79 | udp_rx_buffer: [u8; MAX_UDP_PACKET_SIZE], 80 | udp_tx_buffer: [u8; MAX_UDP_PACKET_SIZE], 81 | _marker: core::marker::PhantomData<&'a ()>, 82 | } 83 | 84 | impl<'a> UdpSocketStorage<'a> { 85 | const fn new() -> Self { 86 | Self { 87 | socket_handle: None, 88 | udp_rx_metadata: [UdpPacketMetadata::EMPTY], 89 | udp_tx_metadata: [UdpPacketMetadata::EMPTY], 90 | udp_rx_buffer: [0u8; MAX_UDP_PACKET_SIZE], 91 | udp_tx_buffer: [0u8; MAX_UDP_PACKET_SIZE], 92 | _marker: core::marker::PhantomData, 93 | } 94 | } 95 | } 96 | 97 | // - types -------------------------------------------------------------------- 98 | 99 | #[derive(Debug)] 100 | pub enum Error { 101 | LinkTimedOut, 102 | ToDo, 103 | } 104 | 105 | // - ethernet::EthernetInterface ---------------------------------------------- 106 | 107 | pub struct EthernetInterface<'a> { 108 | pins: self::Pins, 109 | lan8742a: Option>, 110 | pub interface: Option>>, 111 | _marker: core::marker::PhantomData<&'a ()>, 112 | } 113 | 114 | impl<'a> EthernetInterface<'a> { 115 | fn new(pins: self::Pins) -> Self { 116 | Self { 117 | pins, 118 | lan8742a: None, 119 | interface: None, 120 | _marker: core::marker::PhantomData, 121 | } 122 | } 123 | 124 | pub unsafe fn free( 125 | mut self, 126 | ) -> ( 127 | pins::ethernet::Pins, 128 | hal::ethernet::phy::LAN8742A, 129 | ) { 130 | // halt interrupts 131 | let eth_dma = &*pac::ETHERNET_DMA::ptr(); 132 | eth_dma.dmacier.modify( 133 | |_, w| { 134 | w.nie() 135 | .clear_bit() // normal interrupt summary enable 136 | .rie() 137 | .clear_bit() // receive interrupt enable 138 | .tie() 139 | .clear_bit() 140 | }, // transmit interrupt enable 141 | ); 142 | cortex_m::peripheral::NVIC::mask(pac::Interrupt::ETH); 143 | 144 | // reclaim the objects used to create this structure 145 | let owned_resources = ( 146 | pins::ethernet::Pins { 147 | ref_clk: self.pins.ref_clk.into_analog(), 148 | md_io: self.pins.md_io.into_analog(), 149 | md_clk: self.pins.md_clk.into_analog(), 150 | crs: self.pins.crs.into_analog(), 151 | rx_d0: self.pins.rx_d0.into_analog(), 152 | rx_d1: self.pins.rx_d1.into_analog(), 153 | tx_en: self.pins.tx_en.into_analog(), 154 | tx_d0: self.pins.tx_d0.into_analog(), 155 | tx_d1: self.pins.tx_d1.into_analog(), 156 | }, 157 | core::ptr::replace(&mut self.lan8742a, None).unwrap(), 158 | ); 159 | 160 | // clean out static global singleton 161 | cortex_m::interrupt::free(|cs| { 162 | ETHERNET_MUTEX.borrow(cs).replace(None); 163 | }); 164 | 165 | owned_resources 166 | } 167 | 168 | pub fn start( 169 | pins: pins::ethernet::Pins, 170 | mac_address: &[u8; 6], 171 | ip_address: &[u8; 4], 172 | eth1mac: hal::rcc::rec::Eth1Mac, 173 | ccdr_clocks: &hal::rcc::CoreClocks, 174 | timeout_timer: Timer, 175 | ) -> Result, Error> { 176 | let pins = self::Pins { 177 | ref_clk: pins.ref_clk.into_alternate(), 178 | md_io: pins.md_io.into_alternate(), 179 | md_clk: pins.md_clk.into_alternate(), 180 | crs: pins.crs.into_alternate(), 181 | rx_d0: pins.rx_d0.into_alternate(), 182 | rx_d1: pins.rx_d1.into_alternate(), 183 | tx_en: pins.tx_en.into_alternate(), 184 | tx_d0: pins.tx_d0.into_alternate(), 185 | tx_d1: pins.tx_d1.into_alternate(), 186 | }; 187 | 188 | let mut interface = EthernetInterface::new(pins); 189 | let timeout_timer = match interface.up( 190 | unsafe { &mut *core::ptr::addr_of_mut!(ETHERNET_STORAGE) }, 191 | mac_address, 192 | ip_address, 193 | eth1mac, 194 | ccdr_clocks, 195 | timeout_timer, 196 | ) { 197 | Ok(timeout_timer) => timeout_timer, 198 | Err(e) => { 199 | return Err(e); 200 | } 201 | }; 202 | 203 | // wrap ethernet interface in mutex 204 | cortex_m::interrupt::free(|cs| unsafe { 205 | ETHERNET_MUTEX.borrow(cs).replace(Some(interface)); 206 | }); 207 | 208 | // configure systick timer to 1ms 209 | let syst = unsafe { &mut pac::CorePeripherals::steal().SYST }; 210 | let c_ck_mhz = ccdr_clocks.c_ck().to_MHz(); 211 | let syst_calib = 0x3E8; 212 | syst.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core); 213 | syst.set_reload((syst_calib * c_ck_mhz) - 1); 214 | syst.enable_interrupt(); 215 | syst.enable_counter(); 216 | 217 | Ok(timeout_timer) 218 | } 219 | 220 | pub fn interrupt_free(f: F) -> R 221 | where 222 | F: FnOnce(&mut EthernetInterface<'static>) -> R, 223 | { 224 | cortex_m::interrupt::free(|cs| { 225 | if let Some(ethernet_interface) = 226 | unsafe { ETHERNET_MUTEX.borrow(cs).borrow_mut().as_mut() } 227 | { 228 | f(ethernet_interface) 229 | } else { 230 | panic!("Ethernet interface has not been started"); 231 | } 232 | }) 233 | } 234 | 235 | fn up( 236 | &mut self, 237 | ethernet_storage: &'static mut EthernetStorage<'a>, 238 | mac_address: &[u8; 6], 239 | ip_address: &[u8; 4], 240 | eth1mac: hal::rcc::rec::Eth1Mac, 241 | ccdr_clocks: &hal::rcc::CoreClocks, 242 | mut timeout_timer: Timer, 243 | ) -> Result, Error> { 244 | let dp = unsafe { pac::Peripherals::steal() }; 245 | let ethernet_address = EthernetAddress::from_bytes(mac_address); 246 | let (eth_dma, eth_mac) = unsafe { 247 | ethernet::new_unchecked( 248 | dp.ETHERNET_MAC, 249 | dp.ETHERNET_MTL, 250 | dp.ETHERNET_DMA, 251 | &mut *core::ptr::addr_of_mut!(ETHERNET_DESCRIPTOR_RING), 252 | ethernet_address, 253 | eth1mac, 254 | ccdr_clocks, 255 | ) 256 | }; 257 | 258 | // initialise PHY 259 | let mut lan8742a: hal::ethernet::phy::LAN8742A = 260 | ethernet::phy::LAN8742A::new(eth_mac.set_phy_addr(0)); 261 | lan8742a.phy_reset(); 262 | lan8742a.phy_init(); 263 | 264 | // wait for link to come up 265 | let ten_seconds = core::time::Duration::new(10, 0); 266 | timeout_timer.start(ten_seconds); 267 | let result: Result<(), TimeoutError<()>> = block_timeout!(&mut timeout_timer, { 268 | if lan8742a.poll_link() { 269 | Ok(()) 270 | } else { 271 | Err(nb::Error::WouldBlock) 272 | } 273 | }); 274 | match result { 275 | Ok(()) => (), 276 | Err(TimeoutError::Timeout) | Err(_) => { 277 | return Err(Error::LinkTimedOut); 278 | } 279 | } 280 | 281 | // enable ethernet interrupt 282 | let cp = unsafe { &mut pac::CorePeripherals::steal() }; 283 | unsafe { 284 | ethernet::enable_interrupt(); 285 | cp.NVIC.set_priority(pac::Interrupt::ETH, 196); // mid prio 286 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::ETH); 287 | } 288 | 289 | // -------------------------------------------------------------------- 290 | 291 | // set local ip address 292 | ethernet_storage.ip_addrs = [IpCidr::new(Ipv4Address::from_bytes(ip_address).into(), 0)]; 293 | 294 | let neighbor_cache = NeighborCache::new(&mut ethernet_storage.neighbor_cache_storage[..]); 295 | let routes = Routes::new(&mut ethernet_storage.routes_storage[..]); 296 | let interface = InterfaceBuilder::new(eth_dma, &mut ethernet_storage.socket_storage[..]) 297 | .hardware_addr(ethernet_address.into()) 298 | .neighbor_cache(neighbor_cache) 299 | .ip_addrs(&mut ethernet_storage.ip_addrs[..]) 300 | .routes(routes) 301 | .finalize(); 302 | self.lan8742a = Some(lan8742a); 303 | self.interface = Some(interface); 304 | 305 | Ok(timeout_timer) 306 | } 307 | 308 | pub fn poll_link(&mut self) -> bool { 309 | self.lan8742a.as_mut().unwrap().poll_link() 310 | } 311 | 312 | // poll ethernet interface 313 | pub fn poll(&mut self) -> Result { 314 | let timestamp = smoltcp::time::Instant::from_millis(self.now()); 315 | self.interface.as_mut().unwrap().poll(timestamp) 316 | } 317 | 318 | pub fn poll_delay(&mut self) -> Option { 319 | let timestamp = smoltcp::time::Instant::from_millis(self.now()); 320 | self.interface.as_mut().unwrap().poll_delay(timestamp) 321 | } 322 | 323 | /// returns an absolute time value in milliseconds 324 | pub fn now(&self) -> i64 { 325 | ATOMIC_TIME.load(Ordering::Relaxed).into() 326 | } 327 | 328 | pub fn new_udp_socket(&mut self) -> SocketHandle { 329 | unsafe { 330 | ETHERNET_SOCKETS_STORAGE 331 | .push(UdpSocketStorage::new()) 332 | .unwrap(); // TODO handle result 333 | } 334 | let len = unsafe { ETHERNET_SOCKETS_STORAGE.len() }; 335 | let socket_storage = unsafe { &mut ETHERNET_SOCKETS_STORAGE[len - 1] }; 336 | 337 | let udp_socket = UdpSocket::new( 338 | UdpSocketBuffer::new( 339 | &mut socket_storage.udp_rx_metadata[..], 340 | &mut socket_storage.udp_rx_buffer[..], 341 | ), 342 | UdpSocketBuffer::new( 343 | &mut socket_storage.udp_tx_metadata[..], 344 | &mut socket_storage.udp_tx_buffer[..], 345 | ), 346 | ); 347 | 348 | let socket_handle = self.interface.as_mut().unwrap().add_socket(udp_socket); 349 | socket_handle 350 | } 351 | } 352 | 353 | // - Pins --------------------------------------------------------------------- 354 | 355 | type AlternateFunction11 = hal::gpio::Alternate<11>; 356 | 357 | // Also see: https://github.com/stm32-rs/stm32-eth/blob/master/src/setup.rs 358 | 359 | use hal::gpio::gpioa; 360 | use hal::gpio::gpiob; 361 | use hal::gpio::gpioc; 362 | use hal::gpio::gpiog; 363 | 364 | pub struct Pins { 365 | pub ref_clk: gpioa::PA1, // REFCLK, // RmiiRefClk 366 | pub md_io: gpioa::PA2, // IO, // MDIO 367 | pub md_clk: gpioc::PC1, // CLK, // MDC 368 | pub crs: gpioa::PA7, // CRS, // RmiiCrsDv 369 | pub rx_d0: gpioc::PC4, // RXD0, // RmiiRxD0 370 | pub rx_d1: gpioc::PC5, // RXD1, // RmiiRxD0 371 | pub tx_en: gpiog::PG11, // TXEN, // RmiiTxEN 372 | pub tx_d0: gpiog::PG13, // TXD0, // RmiiTxD0 373 | pub tx_d1: gpiob::PB13, // TXD1, // RmiiTxD1 374 | } 375 | 376 | // - interrupts and exceptions ------------------------------------------------ 377 | 378 | #[interrupt] 379 | fn ETH() { 380 | unsafe { ethernet::interrupt_handler() }; 381 | } 382 | 383 | #[exception] 384 | fn SysTick() { 385 | ATOMIC_TIME.fetch_add(1, Ordering::Relaxed); 386 | } 387 | -------------------------------------------------------------------------------- /examples/dsp/wavetable.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::unreadable_literal)] 2 | #![allow(clippy::excessive_precision)] 3 | 4 | pub const LENGTH: usize = 512; 5 | type Wavetable = [f32; LENGTH]; 6 | 7 | // 512 samples @ 48 KHz = 93.75 Hz 8 | pub static SAW: Wavetable = [ 9 | 0.0000000000, -1.0000000000, -0.7617416840, -0.8973038553, 10 | -0.7952467679, -0.8684955392, -0.8026719089, -0.8521037367, 11 | -0.8031754238, -0.8399762757, -0.8008617851, -0.8298087747, 12 | -0.7971288642, -0.8207023893, -0.7925816186, -0.8122346872, 13 | -0.7875241277, -0.8041811211, -0.7821258594, -0.7964113640, 14 | -0.7764886983, -0.7888446070, -0.7706775647, -0.7814280933, 15 | -0.7647357747, -0.7741259254, -0.7586933308, -0.7669128331, 16 | -0.7525716684, -0.7597705155, -0.7463865062, -0.7526853961, 17 | -0.7401496309, -0.7456471933, -0.7338700518, -0.7386479794, 18 | -0.7275547720, -0.7316815455, -0.7212093155, -0.7247429607, 19 | -0.7148380970, -0.7178282608, -0.7084446861, -0.7109342235, 20 | -0.7020319987, -0.7040582037, -0.6956024399, -0.6971980101, 21 | -0.6891580099, -0.6903518134, -0.6827003850, -0.6835180746, 22 | -0.6762309805, -0.6766954903, -0.6697509985, -0.6698829500, 23 | -0.6632614664, -0.6630795020, -0.6567632669, -0.6562843264, 24 | -0.6502571625, -0.6494967135, -0.6437438146, -0.6427160462, 25 | -0.6372237996, -0.6359417854, -0.6306976220, -0.6291734587, 26 | -0.6241657246, -0.6224106505, -0.6176284979, -0.6156529936, 27 | -0.6110862872, -0.6089001631, -0.6045393989, -0.6021518699, 28 | -0.5979881056, -0.5954078567, -0.5914326506, -0.5886678934, 29 | -0.5848732516, -0.5819317737, -0.5783101040, -0.5751993126, 30 | -0.5717433834, -0.5684703431, -0.5651732484, -0.5617447146, 31 | -0.5585998423, -0.5550222907, -0.5520232948, -0.5483029479, 32 | -0.5454437239, -0.5415865734, -0.5388612371, -0.5348730648, 33 | -0.5322759323, -0.5281623284, -0.5256878993, -0.5214542781, 34 | -0.5190972203, -0.5147488352, -0.5125039710, -0.5080459271, 35 | -0.5059082212, -0.5013454866, -0.4993100353, -0.4946474517, 36 | -0.4927094732, -0.4879517647, -0.4861065904, -0.4812583721, 37 | -0.4795014387, -0.4745672238, -0.4728940666, -0.4678782727, 38 | -0.4662845196, -0.4611914750, -0.4596728404, -0.4545067889, 39 | -0.4530590693, -0.4478241753, -0.4464432448, -0.4411435967, 40 | -0.4398254031, -0.4344650178, -0.4332055789, -0.4277884046, 41 | -0.4265838055, -0.4211137245, -0.4199601147, -0.4144409463, 42 | -0.4133345373, -0.4077700397, -0.4067071030, -0.4011009754, 43 | -0.4000778408, -0.3944337250, -0.3934467786, -0.3877682606, 44 | -0.3868139440, -0.3811045552, -0.3801793639, -0.3744425821, 45 | -0.3735430647, -0.3677823149, -0.3669050725, -0.3611237279, 46 | -0.3602654130, -0.3544667953, -0.3536241118, -0.3478114917, 47 | -0.3469811942, -0.3411577919, -0.3403366855, -0.3345056707, 48 | -0.3336906108, -0.3278551028, -0.3270429953, -0.3212060633, 49 | -0.3203938641, -0.3145585268, -0.3137432424, -0.3079124681, 50 | -0.3070911555, -0.3012678619, -0.3004376290, -0.2946246826, 51 | -0.2937826883, -0.2879829047, -0.2871263592, -0.2813425021, 52 | -0.2804686677, -0.2747034489, -0.2738096399, -0.2680657188, 53 | -0.2671493024, -0.2614292852, -0.2604876816, -0.2547941214, 54 | -0.2538248046, -0.2481602003, -0.2471606985, -0.2415274945, 55 | -0.2404953909, -0.2348959764, -0.2338289095, -0.2282656181, 56 | -0.2271612824, -0.2216363913, -0.2204925379, -0.2150082676, 57 | -0.2138227048, -0.2083812180, -0.2071518121, -0.2017552135, 58 | -0.2004798890, -0.1951302246, -0.1938069652, -0.1885062214, 59 | -0.1871330707, -0.1818831740, -0.1804582355, -0.1752610518, 60 | -0.1737824904, -0.1686398243, -0.1671058661, -0.1620194605, 61 | -0.1604283938, -0.1553999290, -0.1537501048, -0.1487811982, 62 | -0.1470710309, -0.1421632365, -0.1403912042, -0.1355460115, 63 | -0.1337106567, -0.1289294910, -0.1270294211, -0.1223136423, 64 | -0.1203475302, -0.1156984325, -0.1136650168, -0.1090838285, 65 | -0.1069819144, -0.1024697969, -0.1002982562, -0.0958563042, 66 | -0.0936140761, -0.0892433165, -0.0869294078, -0.0826308000, 67 | -0.0802442854, -0.0760187204, -0.0735587433, -0.0694070435, 68 | -0.0668728157, -0.0627957348, -0.0601865372, -0.0561847596, 69 | -0.0534999425, -0.0495740833, -0.0468130665, -0.0429636708, 70 | -0.0401259440, -0.0363534873, -0.0334386101, -0.0297434977, 71 | -0.0267510998, -0.0231336668, -0.0200634484, -0.0165239594, 72 | -0.0133756911, -0.0099143403, -0.0066878632, -0.0033047742, 73 | -0.0000000000, 0.0033047742, 0.0066878632, 0.0099143403, 74 | 0.0133756911, 0.0165239594, 0.0200634484, 0.0231336668, 75 | 0.0267510998, 0.0297434977, 0.0334386101, 0.0363534873, 76 | 0.0401259440, 0.0429636708, 0.0468130665, 0.0495740833, 77 | 0.0534999425, 0.0561847596, 0.0601865372, 0.0627957348, 78 | 0.0668728157, 0.0694070435, 0.0735587433, 0.0760187204, 79 | 0.0802442854, 0.0826308000, 0.0869294078, 0.0892433165, 80 | 0.0936140761, 0.0958563042, 0.1002982562, 0.1024697969, 81 | 0.1069819144, 0.1090838285, 0.1136650168, 0.1156984325, 82 | 0.1203475302, 0.1223136423, 0.1270294211, 0.1289294910, 83 | 0.1337106567, 0.1355460115, 0.1403912042, 0.1421632365, 84 | 0.1470710309, 0.1487811982, 0.1537501048, 0.1553999290, 85 | 0.1604283938, 0.1620194605, 0.1671058661, 0.1686398243, 86 | 0.1737824904, 0.1752610518, 0.1804582355, 0.1818831740, 87 | 0.1871330707, 0.1885062214, 0.1938069652, 0.1951302246, 88 | 0.2004798890, 0.2017552135, 0.2071518121, 0.2083812180, 89 | 0.2138227048, 0.2150082676, 0.2204925379, 0.2216363913, 90 | 0.2271612824, 0.2282656181, 0.2338289095, 0.2348959764, 91 | 0.2404953909, 0.2415274945, 0.2471606985, 0.2481602003, 92 | 0.2538248046, 0.2547941214, 0.2604876816, 0.2614292852, 93 | 0.2671493024, 0.2680657188, 0.2738096399, 0.2747034489, 94 | 0.2804686677, 0.2813425021, 0.2871263592, 0.2879829047, 95 | 0.2937826883, 0.2946246826, 0.3004376290, 0.3012678619, 96 | 0.3070911555, 0.3079124681, 0.3137432424, 0.3145585268, 97 | 0.3203938641, 0.3212060633, 0.3270429953, 0.3278551028, 98 | 0.3336906108, 0.3345056707, 0.3403366855, 0.3411577919, 99 | 0.3469811942, 0.3478114917, 0.3536241118, 0.3544667953, 100 | 0.3602654130, 0.3611237279, 0.3669050725, 0.3677823149, 101 | 0.3735430647, 0.3744425821, 0.3801793639, 0.3811045552, 102 | 0.3868139440, 0.3877682606, 0.3934467786, 0.3944337250, 103 | 0.4000778408, 0.4011009754, 0.4067071030, 0.4077700397, 104 | 0.4133345373, 0.4144409463, 0.4199601147, 0.4211137245, 105 | 0.4265838055, 0.4277884046, 0.4332055789, 0.4344650178, 106 | 0.4398254031, 0.4411435967, 0.4464432448, 0.4478241753, 107 | 0.4530590693, 0.4545067889, 0.4596728404, 0.4611914750, 108 | 0.4662845196, 0.4678782727, 0.4728940666, 0.4745672238, 109 | 0.4795014387, 0.4812583721, 0.4861065904, 0.4879517647, 110 | 0.4927094732, 0.4946474517, 0.4993100353, 0.5013454866, 111 | 0.5059082212, 0.5080459271, 0.5125039710, 0.5147488352, 112 | 0.5190972203, 0.5214542781, 0.5256878993, 0.5281623284, 113 | 0.5322759323, 0.5348730648, 0.5388612371, 0.5415865734, 114 | 0.5454437239, 0.5483029479, 0.5520232948, 0.5550222907, 115 | 0.5585998423, 0.5617447146, 0.5651732484, 0.5684703431, 116 | 0.5717433834, 0.5751993126, 0.5783101040, 0.5819317737, 117 | 0.5848732516, 0.5886678934, 0.5914326506, 0.5954078567, 118 | 0.5979881056, 0.6021518699, 0.6045393989, 0.6089001631, 119 | 0.6110862872, 0.6156529936, 0.6176284979, 0.6224106505, 120 | 0.6241657246, 0.6291734587, 0.6306976220, 0.6359417854, 121 | 0.6372237996, 0.6427160462, 0.6437438146, 0.6494967135, 122 | 0.6502571625, 0.6562843264, 0.6567632669, 0.6630795020, 123 | 0.6632614664, 0.6698829500, 0.6697509985, 0.6766954903, 124 | 0.6762309805, 0.6835180746, 0.6827003850, 0.6903518134, 125 | 0.6891580099, 0.6971980101, 0.6956024399, 0.7040582037, 126 | 0.7020319987, 0.7109342235, 0.7084446861, 0.7178282608, 127 | 0.7148380970, 0.7247429607, 0.7212093155, 0.7316815455, 128 | 0.7275547720, 0.7386479794, 0.7338700518, 0.7456471933, 129 | 0.7401496309, 0.7526853961, 0.7463865062, 0.7597705155, 130 | 0.7525716684, 0.7669128331, 0.7586933308, 0.7741259254, 131 | 0.7647357747, 0.7814280933, 0.7706775647, 0.7888446070, 132 | 0.7764886983, 0.7964113640, 0.7821258594, 0.8041811211, 133 | 0.7875241277, 0.8122346872, 0.7925816186, 0.8207023893, 134 | 0.7971288642, 0.8298087747, 0.8008617851, 0.8399762757, 135 | 0.8031754238, 0.8521037367, 0.8026719089, 0.8684955392, 136 | 0.7952467679, 0.8973038553, 0.7617416840, 1.0000000000, 137 | ]; 138 | 139 | 140 | // 512 samples @ 48 KHz = 93.75 Hz 141 | pub static SIN: Wavetable = [ 142 | 0., 0.012271538, 0.024541229, 0.036807224, 0.049067676, 143 | 0.061320737, 0.07356457, 0.08579731, 0.09801714, 0.110222206, 144 | 0.12241068, 0.1345807, 0.14673047, 0.15885815, 0.17096189, 145 | 0.18303989, 0.19509032, 0.20711137, 0.21910124, 0.2310581, 146 | 0.24298018, 0.25486565, 0.26671275, 0.2785197, 0.29028466, 147 | 0.30200595, 0.31368175, 0.3253103, 0.33688986, 0.34841868, 148 | 0.35989505, 0.3713172, 0.38268343, 0.39399204, 0.4052413, 149 | 0.41642955, 0.42755508, 0.43861625, 0.44961134, 0.46053872, 150 | 0.47139674,0.48218378, 0.4928982, 0.50353837, 0.51410276, 151 | 0.52458966, 0.53499764, 0.545325, 0.55557024, 0.5657318, 152 | 0.57580817, 0.58579785, 0.5956993, 0.60551107, 0.6152316, 153 | 0.6248595, 0.6343933, 0.64383155, 0.65317285, 0.6624158, 0.671559, 154 | 0.680601, 0.68954057, 0.69837624, 0.70710677, 0.71573085, 155 | 0.7242471, 0.7326543, 0.7409511, 0.7491364, 0.7572088, 0.76516724, 156 | 0.77301043, 0.7807372, 0.7883464, 0.7958369, 0.8032075, 157 | 0.81045717, 0.8175848, 0.8245893, 0.8314696, 0.8382247, 0.8448536, 158 | 0.8513552, 0.8577286, 0.86397284, 0.87008697, 0.8760701, 159 | 0.8819213, 0.88763964, 0.8932243, 0.8986745, 0.9039893, 0.909168, 160 | 0.9142098, 0.9191139, 0.9238795, 0.9285061, 0.9329928, 0.937339, 161 | 0.94154406, 0.9456073, 0.94952816, 0.953306, 0.95694035, 162 | 0.9604305, 0.96377605, 0.96697646, 0.97003126, 0.97293997, 163 | 0.9757021, 0.9783174, 0.98078525, 0.9831055, 0.98527765, 164 | 0.9873014, 0.9891765, 0.99090266, 0.99247956, 0.993907, 0.9951847, 165 | 0.9963126, 0.99729043, 0.9981181, 0.99879545, 0.99932235, 166 | 0.9996988, 0.9999247, 1., 0.9999247, 0.9996988, 0.99932235, 167 | 0.99879545, 0.9981181, 0.99729043, 0.9963126, 0.9951847, 0.993907, 168 | 0.99247956, 0.99090266, 0.9891765, 0.9873014, 0.98527765, 169 | 0.9831055, 0.98078525, 0.9783174, 0.9757021, 0.97293997, 170 | 0.97003126, 0.96697646, 0.96377605, 0.9604305, 0.95694035, 171 | 0.953306, 0.94952816, 0.9456073, 0.94154406, 0.937339, 0.9329928, 172 | 0.9285061, 0.9238795, 0.9191139, 0.9142098, 0.909168, 0.9039893, 173 | 0.8986745, 0.8932243, 0.88763964, 0.8819213, 0.8760701, 174 | 0.87008697, 0.86397284, 0.8577286, 0.8513552, 0.8448536, 175 | 0.8382247,0.8314696, 0.8245893, 0.8175848, 0.81045717, 0.8032075, 176 | 0.7958369, 0.7883464, 0.7807372, 0.77301043, 0.76516724, 177 | 0.7572088, 0.7491364, 0.7409511, 0.7326543, 0.7242471, 0.71573085, 178 | 0.70710677, 0.69837624, 0.68954057, 0.680601, 0.671559, 0.6624158, 179 | 0.65317285, 0.64383155, 0.6343933, 0.6248595, 0.6152316, 180 | 0.60551107, 0.5956993, 0.58579785, 0.57580817, 0.5657318, 181 | 0.55557024, 0.545325, 0.53499764, 0.52458966, 0.51410276, 182 | 0.50353837, 0.4928982, 0.48218378, 0.47139674, 0.46053872, 183 | 0.44961134, 0.43861625, 0.42755508, 0.41642955, 0.4052413, 184 | 0.39399204, 0.38268343, 0.3713172, 0.35989505, 0.34841868, 185 | 0.33688986, 0.3253103, 0.31368175, 0.30200595, 0.29028466, 186 | 0.2785197, 0.26671275, 0.25486565, 0.24298018, 0.2310581, 187 | 0.21910124, 0.20711137, 0.19509032, 0.18303989, 0.17096189, 188 | 0.15885815, 0.14673047, 0.1345807, 0.12241068, 0.110222206, 189 | 0.09801714, 0.08579731, 0.07356457, 0.061320737, 0.049067676, 190 | 0.036807224, 0.024541229, 0.012271538, 0.00000000000000012246469, 191 | -0.012271538, -0.024541229, -0.036807224, -0.049067676, 192 | -0.061320737, -0.07356457, -0.08579731, -0.09801714, -0.110222206, 193 | -0.12241068, -0.1345807, -0.14673047, -0.15885815, -0.17096189, 194 | -0.18303989, -0.19509032, -0.20711137, -0.21910124, -0.2310581, 195 | -0.24298018, -0.25486565, -0.26671275, -0.2785197, -0.29028466, 196 | -0.30200595, -0.31368175, -0.3253103, -0.33688986, -0.34841868, 197 | -0.35989505, -0.3713172, -0.38268343, -0.39399204, -0.4052413, 198 | -0.41642955, -0.42755508, -0.43861625, -0.44961134, -0.46053872, 199 | -0.47139674, -0.48218378, -0.4928982, -0.50353837, -0.51410276, 200 | -0.52458966, -0.53499764, -0.545325, -0.55557024, -0.5657318, 201 | -0.57580817, -0.58579785, -0.5956993, -0.60551107, -0.6152316, 202 | -0.6248595, -0.6343933, -0.64383155, -0.65317285, -0.6624158, 203 | -0.671559, -0.680601, -0.68954057, -0.69837624, -0.70710677, 204 | -0.71573085,-0.7242471, -0.7326543, -0.7409511, -0.7491364, 205 | -0.7572088, -0.76516724, -0.77301043, -0.7807372, -0.7883464, 206 | -0.7958369, -0.8032075, -0.81045717, -0.8175848, -0.8245893, 207 | -0.8314696, -0.8382247, -0.8448536,-0.8513552, -0.8577286, 208 | -0.86397284, -0.87008697, -0.8760701, -0.8819213, -0.88763964, 209 | -0.8932243, -0.8986745, -0.9039893, -0.909168, -0.9142098, 210 | -0.9191139, -0.9238795, -0.9285061, -0.9329928, -0.937339, 211 | -0.94154406, -0.9456073, -0.94952816, -0.953306, -0.95694035, 212 | -0.9604305, -0.96377605, -0.96697646, -0.97003126, -0.97293997, 213 | -0.9757021, -0.9783174, -0.98078525, -0.9831055, -0.98527765, 214 | -0.9873014, -0.9891765, -0.99090266, -0.99247956, -0.993907, 215 | -0.9951847, -0.9963126, -0.99729043, -0.9981181, -0.99879545, 216 | -0.99932235, -0.9996988, -0.9999247, -1., -0.9999247, -0.9996988, 217 | -0.99932235, -0.99879545, -0.9981181, -0.99729043, -0.9963126, 218 | -0.9951847, -0.993907, -0.99247956, -0.99090266, -0.9891765, 219 | -0.9873014, -0.98527765, -0.9831055, -0.98078525, -0.9783174, 220 | -0.9757021, -0.97293997, -0.97003126, -0.96697646, -0.96377605, 221 | -0.9604305, -0.95694035, -0.953306, -0.94952816, -0.9456073, 222 | -0.94154406, -0.937339, -0.9329928, -0.9285061, -0.9238795, 223 | -0.9191139, -0.9142098, -0.909168, -0.9039893, -0.8986745, 224 | -0.8932243, -0.88763964, -0.8819213, -0.8760701, -0.87008697, 225 | -0.86397284, -0.8577286, -0.8513552, -0.8448536, -0.8382247, 226 | -0.8314696, -0.8245893, -0.8175848, -0.81045717, -0.8032075, 227 | -0.7958369, -0.7883464, -0.7807372, -0.77301043, -0.76516724, 228 | -0.7572088, -0.7491364, -0.7409511, -0.7326543, -0.7242471, 229 | -0.71573085, -0.70710677, -0.69837624, -0.68954057, 230 | -0.680601,-0.671559, -0.6624158, -0.65317285, -0.64383155, 231 | -0.6343933, -0.6248595, -0.6152316, -0.60551107, -0.5956993, 232 | -0.58579785, -0.57580817, -0.5657318, -0.55557024, -0.545325, 233 | -0.53499764, -0.52458966, -0.51410276, -0.50353837, -0.4928982, 234 | -0.48218378, -0.47139674, -0.46053872, -0.44961134, -0.43861625, 235 | -0.42755508, -0.41642955, -0.4052413, -0.39399204, -0.38268343, 236 | -0.3713172, -0.35989505, -0.34841868, -0.33688986, -0.3253103, 237 | -0.31368175, -0.30200595, -0.29028466, -0.2785197, -0.26671275, 238 | -0.25486565, -0.24298018, -0.2310581, -0.21910124, -0.20711137, 239 | -0.19509032, -0.18303989, -0.17096189, -0.15885815, 240 | -0.14673047,-0.1345807, -0.12241068, -0.110222206, -0.09801714, 241 | -0.08579731, -0.07356457, -0.061320737, -0.049067676, 242 | -0.036807224, -0.024541229, -0.012271538 243 | ]; 244 | 245 | /* 246 | pub fn generate_sin() { 247 | let length = 512; 248 | let range = 1.; 249 | let pi = std::f64::consts::PI; 250 | for x in 0..length { 251 | let phase = (x as f64 / length as f64) * pi * 2.; 252 | let y = phase.sin(); 253 | print!("{}, ", (y * range) as f32); 254 | } 255 | } 256 | */ 257 | --------------------------------------------------------------------------------