diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 6c49f376..8163624f 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -10,7 +10,7 @@ embassy = { version = "0.1.0", path = "../embassy" } embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"] } embassy-extras = {version = "0.1.0", path = "../embassy-extras" } embassy-traits = {version = "0.1.0", path = "../embassy-traits" } -embassy-net = { version = "0.1.0", path = "../embassy-net", features = ["tcp", "medium-ethernet"], optional = true } +embassy-net = { version = "0.1.0", path = "../embassy-net", default-features = false, optional = true } defmt = { version = "0.2.0", optional = true } log = { version = "0.4.11", optional = true } diff --git a/examples/stm32h7/.cargo/config b/examples/stm32h7/.cargo/config new file mode 100644 index 00000000..6b6a9f50 --- /dev/null +++ b/examples/stm32h7/.cargo/config @@ -0,0 +1,10 @@ +[target.thumbv7em-none-eabihf] +runner = 'probe-run --chip STM32H743ZITx' +rustflags = [ + # LLD (shipped with the Rust toolchain) is used as the default linker + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", +] + +[build] +target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index d7288e4c..ebaa4e5d 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -19,8 +19,11 @@ defmt-error = [] [dependencies] embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-trace"] } embassy-traits = { version = "0.1.0", path = "../../embassy-traits", features = ["defmt"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "defmt-trace", "stm32h743zi"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "defmt-trace", "stm32h743zi", "net"] } embassy-extras = {version = "0.1.0", path = "../../embassy-extras" } +embassy-net = { path = "../../embassy-net", default-features = false, features = ["defmt-debug", "defmt", "tcp", "medium-ethernet", "pool-16"] } +stm32-metapac = { path = "../../stm32-metapac", features = ["stm32h743zi"] } +embassy-macros = { path = "../../embassy-macros" } stm32h7 = { version = "0.13", features = ["stm32h743"]} stm32h7xx-hal = { version = "0.9.0", features = ["stm32h743"] } @@ -34,6 +37,18 @@ panic-probe = { version = "0.2.0", features= ["print-defmt"] } futures = { version = "0.3.8", default-features = false, features = ["async-await"] } rtt-target = { version = "0.3", features = ["cortex-m"] } heapless = { version = "0.7.1", default-features = false } +rand_core = { version = "0.6.2" } +critical-section = "0.2.1" micromath = "2.0.0" +[dependencies.smoltcp] +git = "https://github.com/smoltcp-rs/smoltcp" +rev = "e4241510337e095b9d21136c5f58b2eaa1b78479" +default-features = false +features = [ + "proto-ipv4", + "socket", + "async", + "defmt", +] diff --git a/examples/stm32h7/build.rs b/examples/stm32h7/build.rs new file mode 100644 index 00000000..555cdf68 --- /dev/null +++ b/examples/stm32h7/build.rs @@ -0,0 +1,21 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); +} diff --git a/examples/stm32h7/memory.x b/examples/stm32h7/memory.x index 48f58e36..ef9485d1 100644 --- a/examples/stm32h7/memory.x +++ b/examples/stm32h7/memory.x @@ -1,5 +1,5 @@ MEMORY { FLASH : ORIGIN = 0x08000000, LENGTH = 2048K - RAM : ORIGIN = 0x20000000, LENGTH = 128K + RAM : ORIGIN = 0x24000000, LENGTH = 384K } diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs new file mode 100644 index 00000000..57997da0 --- /dev/null +++ b/examples/stm32h7/src/bin/eth.rs @@ -0,0 +1,167 @@ +#![no_std] +#![no_main] +#![feature(trait_alias)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] +#![feature(type_alias_impl_trait)] + +use core::pin::Pin; +use core::sync::atomic::{AtomicUsize, Ordering}; + +use cortex_m_rt::entry; +use defmt::{info, unwrap}; +use defmt_rtt as _; // global logger +use embassy::executor::{Executor, Spawner}; +use embassy::io::AsyncWriteExt; +use embassy::time::{Duration, Timer}; +use embassy::util::Forever; +use embassy_macros::interrupt_take; +use embassy_net::{Config as NetConfig, Ipv4Address, Ipv4Cidr, StaticConfigurator, TcpSocket}; +use embassy_stm32::clock::{Alarm, Clock}; +use embassy_stm32::eth::lan8742a::LAN8742A; +use embassy_stm32::eth::Ethernet; +use embassy_stm32::rcc::{Config as RccConfig, Rcc}; +use embassy_stm32::rng::Random; +use embassy_stm32::time::Hertz; +use embassy_stm32::{interrupt, peripherals, Config}; +use heapless::Vec; +use panic_probe as _; +use peripherals::{RNG, TIM2}; + +defmt::timestamp! {"{=u64}", { + static COUNT: AtomicUsize = AtomicUsize::new(0); + // NOTE(no-CAS) `timestamps` runs with interrupts disabled + let n = COUNT.load(Ordering::Relaxed); + COUNT.store(n + 1, Ordering::Relaxed); + n as u64 + } +} + +#[embassy::task] +async fn main_task( + device: &'static mut Pin<&'static mut Ethernet<'static, LAN8742A, 4, 4>>, + config: &'static mut StaticConfigurator, + spawner: Spawner, +) { + // Init network stack + embassy_net::init(device, config); + + // Launch network task + unwrap!(spawner.spawn(net_task())); + + info!("Network task initialized"); + + // Then we can use it! + let mut rx_buffer = [0; 1024]; + let mut tx_buffer = [0; 1024]; + let mut socket = TcpSocket::new(&mut rx_buffer, &mut tx_buffer); + + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + let remote_endpoint = (Ipv4Address::new(192, 168, 0, 10), 8000); + let r = socket.connect(remote_endpoint).await; + if let Err(e) = r { + info!("connect error: {:?}", e); + return; + } + info!("connected!"); + loop { + let r = socket.write_all(b"Hello\n").await; + if let Err(e) = r { + info!("write error: {:?}", e); + return; + } + Timer::after(Duration::from_secs(1)).await; + } +} + +#[embassy::task] +async fn net_task() { + embassy_net::run().await +} + +#[no_mangle] +fn _embassy_rand(buf: &mut [u8]) { + use rand_core::RngCore; + + critical_section::with(|_| unsafe { + unwrap!(RNG_INST.as_mut()).fill_bytes(buf); + }); +} + +static mut RNG_INST: Option> = None; + +static EXECUTOR: Forever = Forever::new(); +static TIMER_RTC: Forever> = Forever::new(); +static ALARM: Forever> = Forever::new(); +static ETH: Forever> = Forever::new(); +static DEVICE: Forever>> = Forever::new(); +static CONFIG: Forever = Forever::new(); + +#[entry] +fn main() -> ! { + use stm32_metapac::RCC; + + info!("Hello World!"); + + info!("Setup RCC..."); + let mut rcc_config = RccConfig::default(); + rcc_config.sys_ck = Some(Hertz(400_000_000)); + rcc_config.pll1.q_ck = Some(Hertz(100_000_000)); + let config = Config::default().rcc(rcc_config); + + let mut p = embassy_stm32::init(config); + + // Constrain and Freeze clock + + let mut rcc = Rcc::new(&mut p.RCC, RccConfig::default()); + rcc.enable_debug_wfe(&mut p.DBGMCU, true); + + unsafe { + RCC.ahb4enr().modify(|w| { + w.set_gpioaen(true); + w.set_gpioben(true); + w.set_gpiocen(true); + w.set_gpioden(true); + w.set_gpioien(true); + }); + } + + let rtc_int = interrupt_take!(TIM2); + let rtc = TIMER_RTC.put(Clock::new(p.TIM2, rtc_int)); + rtc.start(); + let alarm = ALARM.put(rtc.alarm1()); + + unsafe { embassy::time::set_clock(rtc) }; + + let rng = Random::new(p.RNG); + unsafe { + RNG_INST.replace(rng); + } + + let eth_int = interrupt_take!(ETH); + let mac_addr = [0x10; 6]; + let eth = ETH.put(Ethernet::new( + p.ETH, eth_int, p.PA1, p.PA2, p.PC1, p.PA7, p.PC4, p.PC5, p.PB12, p.PB13, p.PB11, LAN8742A, + mac_addr, 1, + )); + + // NOTE(unsafe) This thing is a &'static + let net_device = DEVICE.put(unsafe { Pin::new_unchecked(eth) }); + net_device.as_mut().init(); + + let config = StaticConfigurator::new(NetConfig { + address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 0, 61), 24), + dns_servers: Vec::new(), + gateway: Some(Ipv4Address::new(192, 168, 0, 1)), + }); + + let config = CONFIG.put(config); + + let executor = EXECUTOR.put(Executor::new()); + executor.set_alarm(alarm); + + executor.run(move |spawner| { + unwrap!(spawner.spawn(main_task(net_device, config, spawner))); + }) +}