From 11030e2137b7179bcfb781cdbb01110989500428 Mon Sep 17 00:00:00 2001 From: Tobias Breitwieser Date: Sat, 21 Oct 2023 17:17:02 +0200 Subject: [PATCH] add example --- Cargo.toml | 7 +- .../embassy-stm32-example/.cargo/config.toml | 8 + examples/embassy-stm32-example/Cargo.toml | 28 +++ .../embassy-stm32-example/rust-toolchain.toml | 14 ++ examples/embassy-stm32-example/src/main.rs | 180 ++++++++++++++++++ src/asynch/runner.rs | 152 +++++++-------- src/asynch/state.rs | 2 - src/error.rs | 2 +- 8 files changed, 306 insertions(+), 87 deletions(-) create mode 100644 examples/embassy-stm32-example/.cargo/config.toml create mode 100644 examples/embassy-stm32-example/Cargo.toml create mode 100644 examples/embassy-stm32-example/rust-toolchain.toml create mode 100644 examples/embassy-stm32-example/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 27adde0..06a749d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ defmt = [ "ublox-sockets?/defmt", "atat/defmt", "heapless/defmt-impl", + "embassy-time/defmt", ] log = ["dep:log", "ublox-sockets?/log", "atat/log"] @@ -79,11 +80,5 @@ default-members = ["."] exclude = ["examples"] [patch.crates-io] -#atat = { git = "https://github.com/BlackbirdHQ/atat", rev = "d2175b4", features = [ -# "derive", -# "defmt", -# "bytes", -#] } -# ublox-sockets = { git = "https://github.com/BlackbirdHQ/ublox-sockets", rev = "1b59385" } ublox-sockets = { path = "../ublox-sockets" } no-std-net = { git = "https://github.com/rushmorem/no-std-net", branch = "issue-15" } diff --git a/examples/embassy-stm32-example/.cargo/config.toml b/examples/embassy-stm32-example/.cargo/config.toml new file mode 100644 index 0000000..25c56a2 --- /dev/null +++ b/examples/embassy-stm32-example/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.thumbv7em-none-eabihf] +runner = 'probe-rs run --chip STM32H747XIHx' + +[build] +target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) + +[env] +DEFMT_LOG = "trace" diff --git a/examples/embassy-stm32-example/Cargo.toml b/examples/embassy-stm32-example/Cargo.toml new file mode 100644 index 0000000..2c909a7 --- /dev/null +++ b/examples/embassy-stm32-example/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "embassy-stm32-example" +version = "0.1.0" +edition = "2021" + +# Example for the STM32H747I-DISCO board with SARA-R5 modem attached to UART8 +[dependencies] +embassy-stm32 = { version = "0.1.0", git = "https://github.com/embassy-rs/embassy.git", branch = "main", features = ["nightly", "defmt", "stm32h747xi-cm7", "time-driver-any", "exti", "memory-x", "unstable-pac", "unstable-traits"] } +embassy-executor = { version = "0.3.0", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-time = { version = "0.1", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "nightly"] } +embassy-sync = { version = "0.3.0", features = ["defmt"] } + + +cortex-m = { version = "0.7.7", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.3" + +defmt = "0.3.5" +defmt-rtt = "0.4.0" +panic-probe = { version = "0.3.1", features = ["print-defmt"] } + +static_cell = { version = "1.2.0", features = ["nightly"]} + +atat = { version = "0.20.0", features = ["derive", "bytes", "defmt"] } +ublox-cellular-rs = {version = "0.4.0", path = "../..", features = ["sara-r5", "defmt", "async"]} + +[patch.crates-io] +ublox-sockets = { path = "../../../ublox-sockets" } +no-std-net = { git = "https://github.com/rushmorem/no-std-net", branch = "issue-15" } diff --git a/examples/embassy-stm32-example/rust-toolchain.toml b/examples/embassy-stm32-example/rust-toolchain.toml new file mode 100644 index 0000000..e33f5e0 --- /dev/null +++ b/examples/embassy-stm32-example/rust-toolchain.toml @@ -0,0 +1,14 @@ +# Before upgrading check that everything is available on all tier1 targets here: +# https://rust-lang.github.io/rustup-components-history +[toolchain] +channel = "nightly-2023-10-02" +components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] +targets = [ + "thumbv7em-none-eabi", + "thumbv7m-none-eabi", + "thumbv6m-none-eabi", + "thumbv7em-none-eabihf", + "thumbv8m.main-none-eabihf", + "riscv32imac-unknown-none-elf", + "wasm32-unknown-unknown", +] \ No newline at end of file diff --git a/examples/embassy-stm32-example/src/main.rs b/examples/embassy-stm32-example/src/main.rs new file mode 100644 index 0000000..acb443c --- /dev/null +++ b/examples/embassy-stm32-example/src/main.rs @@ -0,0 +1,180 @@ +#![no_std] +#![no_main] +#![allow(stable_features)] +#![feature(type_alias_impl_trait)] + +use core::cell::RefCell; +use cortex_m_rt::entry; +use defmt::*; +use embassy_executor::{Executor, Spawner}; +use embassy_stm32::gpio::{AnyPin, Input, Level, Output, Pin, Pull, Speed}; +use embassy_stm32::rcc::VoltageScale; +use embassy_stm32::time::{khz, mhz}; +use embassy_stm32::{bind_interrupts, peripherals, Config, interrupt, usart}; +use embassy_stm32::peripherals::UART8; +use embassy_stm32::usart::{BufferedUart, BufferedUartRx, BufferedUartTx}; +use embassy_time::{Duration, Timer}; +use static_cell::StaticCell; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + + +// use embedded_hal::digital::{ErrorType, InputPin, OutputPin}; + +use ublox_cellular; +use ublox_cellular::config::CellularConfig; + +use atat::{Buffers, DefaultDigester, Ingress, AtatIngress, AtDigester, Parser}; +use atat::asynch::AtatClient; +use ublox_cellular::asynch::runner::Runner; +use ublox_cellular::asynch::State; +use ublox_cellular::command::{AT, Urc}; + + +bind_interrupts!(struct Irqs { + UART8 => embassy_stm32::usart::BufferedInterruptHandler; +}); + +const INGRESS_BUF_SIZE: usize = 1024; +const URC_CAPACITY: usize = 2; +const URC_SUBSCRIBERS: usize = 2; + +struct MyCelullarConfig { + reset_pin: Option>, + // reset_pin: Option, + power_pin: Option>, + // power_pin: Option, + vint_pin: Option>, + // vint_pin: Option +} + +impl CellularConfig for MyCelullarConfig { + type ResetPin = Output<'static, AnyPin>; + // type ResetPin = NoPin; + type PowerPin = Output<'static, AnyPin>; + // type PowerPin = NoPin; + type VintPin = Input<'static, AnyPin>; + // type VintPin = NoPin; + + const FLOW_CONTROL: bool = false; + const HEX_MODE: bool = true; + fn reset_pin(&mut self) -> Option<&mut Self::ResetPin> { + info!("reset_pin"); + return self.reset_pin.as_mut() + } + fn power_pin(&mut self) -> Option<&mut Self::PowerPin> { + info!("power_pin"); + return self.power_pin.as_mut() + } + fn vint_pin(&mut self) -> Option<&mut Self::VintPin> { + info!("vint_pin = {}", self.vint_pin.as_mut()?.is_high()); + return self.vint_pin.as_mut() + } +} + +#[embassy_executor::task] +async fn main_task(spawner: Spawner) { + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL50, + divp: Some(PllDiv::DIV2), + divq: Some(PllDiv::DIV8), // used by SPI3. 100Mhz. + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } + let p = embassy_stm32::init(config); + + let led1_pin = p.PI12.degrade(); + let led2_pin = p.PI13.degrade(); + let led3_pin = p.PI14.degrade(); + + + + let tx_buf = &mut make_static!([0u8; 16])[..]; + let rx_buf = &mut make_static!([0u8; 16])[..]; + let (tx_pin, rx_pin, uart) = (p.PJ8, p.PJ9, p.UART8); + let mut uart_config = embassy_stm32::usart::Config::default(); + { + uart_config.baudrate = 115200; + uart_config.baudrate = 9600; + uart_config.parity = embassy_stm32::usart::Parity::ParityNone; + uart_config.stop_bits = embassy_stm32::usart::StopBits::STOP1; + uart_config.data_bits = embassy_stm32::usart::DataBits::DataBits8; + } + + + let uart = BufferedUart::new( + uart, + Irqs, + rx_pin, + tx_pin, + tx_buf, + rx_buf, + uart_config, + ); + let (mut writer, reader) = uart.unwrap().split(); + // let power = Output::new(p.PJ4, Level::High, Speed::VeryHigh).degrade(); + // let reset = Output::new(p.PF8, Level::High, Speed::VeryHigh).degrade(); + let celullar_config = MyCelullarConfig { + reset_pin: Some(Output::new(p.PF8, Level::High, Speed::VeryHigh).degrade()), + power_pin: Some(Output::new(p.PJ4, Level::High, Speed::VeryHigh).degrade()), + vint_pin: Some(Input::new(p.PJ3, Pull::Down).degrade()) + }; + + let buffers = &*make_static!(atat::Buffers::new()); + let (ingress, client) = buffers.split(writer, AtDigester::default(), atat::Config::new()); + + spawner.spawn(ingress_task(ingress, reader)).unwrap(); + + let state = make_static!(State::new(client)); + let (device, mut control, mut runner) = ublox_cellular::asynch::new(state, &buffers.urc_channel, celullar_config).await; + + runner.init().await.unwrap(); + defmt::unwrap!(spawner.spawn(cellular_task(runner))); + + loop { + Timer::after(Duration::from_millis(1000)).await; + } +} + + +#[embassy_executor::task] +async fn ingress_task( + mut ingress: Ingress<'static, DefaultDigester, ublox_cellular::command::Urc, {INGRESS_BUF_SIZE}, {URC_CAPACITY}, {URC_SUBSCRIBERS}>, + mut reader: BufferedUartRx<'static, UART8>, +) -> ! { + ingress.read_from(&mut reader).await +} + +#[embassy_executor::task] +async fn cellular_task( + runner: Runner<'static, atat::asynch::Client<'_, BufferedUartTx<'static, UART8>, {INGRESS_BUF_SIZE}>, MyCelullarConfig, {URC_CAPACITY}>, +) -> ! { + runner.run().await +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let executor = EXECUTOR.init(Executor::new()); + + executor.run(|spawner| { + unwrap!(spawner.spawn(main_task(spawner))); + }) +} \ No newline at end of file diff --git a/src/asynch/runner.rs b/src/asynch/runner.rs index 729fb6f..efc004e 100644 --- a/src/asynch/runner.rs +++ b/src/asynch/runner.rs @@ -3,30 +3,31 @@ use core::str::FromStr; use crate::{command::Urc, config::CellularConfig}; use super::state::{self, LinkState}; -use crate::error::Error; -use crate::error::GenericError::Timeout; -use crate::module_timing::{boot_time, reset_time}; -use atat::{asynch::AtatClient, UrcSubscription}; -use embassy_time::{with_timeout, Duration, Timer}; -use embedded_hal::digital::{InputPin, OutputPin}; -use futures::future::ok; -use no_std_net::{Ipv4Addr, Ipv6Addr}; use crate::asynch::state::PowerState; -use crate::command::AT; -use crate::command::control::{SetCircuit108Behaviour, SetCircuit109Behaviour, SetFlowControl}; use crate::command::control::types::{Circuit108Behaviour, Circuit109Behaviour, FlowControl}; -use crate::command::device_lock::GetPinStatus; +use crate::command::control::{SetCircuit108Behaviour, SetCircuit109Behaviour, SetFlowControl}; use crate::command::device_lock::responses::PinStatus; use crate::command::device_lock::types::PinStatusCode; -use crate::command::general::{GetCCID, GetFirmwareVersion, GetModelId}; -use crate::command::gpio::SetGpioConfiguration; +use crate::command::device_lock::GetPinStatus; +use crate::command::general::{GetCCID, GetFirmwareVersion, GetModelId, IdentificationInformation}; use crate::command::gpio::types::{GpioInPull, GpioMode, GpioOutValue}; -use crate::command::ip_transport_layer::SetHexMode; +use crate::command::gpio::SetGpioConfiguration; use crate::command::ip_transport_layer::types::HexMode; -use crate::command::mobile_control::{SetModuleFunctionality, SetReportMobileTerminationError}; +use crate::command::ip_transport_layer::SetHexMode; use crate::command::mobile_control::types::{Functionality, ResetMode, TerminationErrorMode}; -use crate::command::system_features::SetPowerSavingControl; +use crate::command::mobile_control::{SetModuleFunctionality, SetReportMobileTerminationError}; use crate::command::system_features::types::PowerSavingMode; +use crate::command::system_features::SetPowerSavingControl; +use crate::command::AT; +use crate::error::Error; +use crate::error::GenericError::Timeout; +use crate::module_timing::{boot_time, reset_time}; +use atat::{asynch::AtatClient, UrcSubscription}; +use embassy_time::{with_timeout, Duration, Timer}; +use embedded_hal::digital::{InputPin, OutputPin}; +use futures::future::ok; +use heapless::String; +use no_std_net::{Ipv4Addr, Ipv6Addr}; use super::AtHandle; @@ -58,14 +59,14 @@ impl<'d, AT: AtatClient, C: CellularConfig, const URC_CAPACITY: usize> } // TODO: crate visibility only makes sense if reset and co are also crate visibility - pub(crate) async fn init(&mut self) -> Result<(), Error> { + // pub(crate) async fn init(&mut self) -> Result<(), Error> { + pub async fn init(&mut self) -> Result<(), Error> { // Initilize a new ublox device to a known state (set RS232 settings) debug!("Initializing module"); // Hard reset module self.reset().await?; self.is_alive().await?; - Ok(()) } @@ -78,7 +79,7 @@ impl<'d, AT: AtatClient, C: CellularConfig, const URC_CAPACITY: usize> Ok(_) => { self.ch.set_power_state(PowerState::Alive); Ok(true) - }, + } Err(err) => return Err(Error::Atat(err)), }; alive @@ -106,97 +107,92 @@ impl<'d, AT: AtatClient, C: CellularConfig, const URC_CAPACITY: usize> } // Extended errors on - self.at.send( - SetReportMobileTerminationError { + self.at + .send(SetReportMobileTerminationError { n: TerminationErrorMode::Enabled, - } - ).await?; + }) + .await?; // Select SIM - self.at.send( - SetGpioConfiguration { + self.at + .send(SetGpioConfiguration { gpio_id: 25, gpio_mode: GpioMode::Output(GpioOutValue::High), - } - ).await?; + }) + .await?; #[cfg(any(feature = "lara-r6"))] - self.at.send( - SetGpioConfiguration { + self.at + .send(SetGpioConfiguration { gpio_id: 42, gpio_mode: GpioMode::Input(GpioInPull::NoPull), - }, - ).await?; + }) + .await?; - self.at.send( - GetModelId).await?; + let model_id = self.at.send(GetModelId).await?; - // self.network.send_internal( + // self.at.send( // &IdentificationInformation { // n: 9 // }, - // false, - // )?; + // ).await?; - self.at.send( - GetFirmwareVersion).await?; + self.at.send(GetFirmwareVersion).await?; self.select_sim_card().await?; - self.at.send( - GetCCID).await?; - + let ccid = self.at.send(GetCCID).await?; + info!("CCID: {}", ccid.ccid); // DCD circuit (109) changes in accordance with the carrier - self.at.send( - - SetCircuit109Behaviour { + self.at + .send(SetCircuit109Behaviour { value: Circuit109Behaviour::ChangesWithCarrier, - }, - ).await?; + }) + .await?; // Ignore changes to DTR - self.at.send( - SetCircuit108Behaviour { + self.at + .send(SetCircuit108Behaviour { value: Circuit108Behaviour::Ignore, - }, - ).await?; + }) + .await?; // Switch off UART power saving until it is integrated into this API - self.at.send( - SetPowerSavingControl { + self.at + .send(SetPowerSavingControl { mode: PowerSavingMode::Disabled, timeout: None, - }, - ).await?; + }) + .await?; if C::HEX_MODE { - self.at.send( - SetHexMode { + self.at + .send(SetHexMode { hex_mode_disable: HexMode::Enabled, - }, - ).await?; + }) + .await?; } else { - self.at.send( - SetHexMode { + self.at + .send(SetHexMode { hex_mode_disable: HexMode::Disabled, - }, - ).await?; + }) + .await?; } // Tell module whether we support flow control // FIXME: Use AT+IFC=2,2 instead of AT&K here if C::FLOW_CONTROL { - self.at.send( - SetFlowControl { + self.at + .send(SetFlowControl { value: FlowControl::RtsCts, - }, - ).await?; + }) + .await?; } else { - self.at.send( - SetFlowControl { + self.at + .send(SetFlowControl { value: FlowControl::Disabled, - }, - ).await?; + }) + .await?; } self.ch.set_power_state(PowerState::Initialized); @@ -219,22 +215,22 @@ impl<'d, AT: AtatClient, C: CellularConfig, const URC_CAPACITY: usize> // There was an error initializing the SIM // We've seen issues on uBlox-based devices, as a precation, we'll cycle // the modem here through minimal/full functional state. - self.at.send( - SetModuleFunctionality { + self.at + .send(SetModuleFunctionality { fun: Functionality::Minimum, // SARA-R5 This parameter can be used only when is 1, 4 or 19 #[cfg(feature = "sara-r5")] rst: None, #[cfg(not(feature = "sara-r5"))] rst: Some(ResetMode::DontReset), - }, - ).await?; - self.at.send( - SetModuleFunctionality { + }) + .await?; + self.at + .send(SetModuleFunctionality { fun: Functionality::Full, rst: Some(ResetMode::DontReset), - }, - ).await?; + }) + .await?; Err(Error::Busy) } diff --git a/src/asynch/state.rs b/src/asynch/state.rs index 3e16947..cfd67ba 100644 --- a/src/asynch/state.rs +++ b/src/asynch/state.rs @@ -30,7 +30,6 @@ pub enum PowerState { Initialized, } - use crate::command::Urc; use super::AtHandle; @@ -89,7 +88,6 @@ impl<'d> Runner<'d> { s.waker.wake(); }); } - } impl<'d> StateRunner<'d> { diff --git a/src/error.rs b/src/error.rs index 35590f4..5801426 100644 --- a/src/error.rs +++ b/src/error.rs @@ -55,4 +55,4 @@ impl From for Error { fn from(e: atat::Error) -> Self { Self::Atat(e) } -} \ No newline at end of file +}