From 7ad6280e6575fc400f84caa5c26eb3ba9770877f Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 31 Aug 2021 14:32:48 +0200 Subject: Add HAL for SubGhz peripheral for STM32 WL series Based on the HAL from stm32wl, the peripheral driver has been modified to fit into embassy, using the embassy APIs, providing operation of the radio peripheral. The initial version does not offer any async APIs, but the example shows how the radio IRQ can be used to perform async TX of the radio. --- examples/stm32wl55/Cargo.toml | 2 +- examples/stm32wl55/src/bin/subghz.rs | 129 +++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 examples/stm32wl55/src/bin/subghz.rs (limited to 'examples') diff --git a/examples/stm32wl55/Cargo.toml b/examples/stm32wl55/Cargo.toml index a7313e33f..1bdfe9bc9 100644 --- a/examples/stm32wl55/Cargo.toml +++ b/examples/stm32wl55/Cargo.toml @@ -19,7 +19,7 @@ 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", "stm32wl55jc_cm4", "time-driver-tim2", "memory-x"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "defmt-trace", "stm32wl55jc_cm4", "time-driver-tim2", "memory-x", "subghz"] } embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } defmt = "0.2.0" diff --git a/examples/stm32wl55/src/bin/subghz.rs b/examples/stm32wl55/src/bin/subghz.rs new file mode 100644 index 000000000..1e406886a --- /dev/null +++ b/examples/stm32wl55/src/bin/subghz.rs @@ -0,0 +1,129 @@ +#![no_std] +#![no_main] +#![macro_use] +#![allow(dead_code)] +#![feature(generic_associated_types)] +#![feature(type_alias_impl_trait)] + +#[path = "../example_common.rs"] +mod example_common; + +use embassy::{traits::gpio::WaitForRisingEdge, util::InterruptFuture}; +use embassy_stm32::{ + dbgmcu::Dbgmcu, + dma::NoDma, + exti::ExtiInput, + gpio::{Input, Level, Output, Pull, Speed}, + interrupt, + subghz::*, + Peripherals, +}; +use embedded_hal::digital::v2::OutputPin; +use example_common::unwrap; + +const PING_DATA: &str = "PING"; +const DATA_LEN: u8 = PING_DATA.len() as u8; +const PING_DATA_BYTES: &[u8] = PING_DATA.as_bytes(); +const PREAMBLE_LEN: u16 = 5 * 8; + +const RF_FREQ: RfFreq = RfFreq::from_frequency(867_500_000); + +const SYNC_WORD: [u8; 8] = [0x79, 0x80, 0x0C, 0xC0, 0x29, 0x95, 0xF8, 0x4A]; +const SYNC_WORD_LEN: u8 = SYNC_WORD.len() as u8; +const SYNC_WORD_LEN_BITS: u8 = SYNC_WORD_LEN * 8; + +const TX_BUF_OFFSET: u8 = 128; +const RX_BUF_OFFSET: u8 = 0; +const LORA_PACKET_PARAMS: LoRaPacketParams = LoRaPacketParams::new() + .set_crc_en(true) + .set_preamble_len(PREAMBLE_LEN) + .set_payload_len(DATA_LEN) + .set_invert_iq(false) + .set_header_type(HeaderType::Fixed); + +const LORA_MOD_PARAMS: LoRaModParams = LoRaModParams::new() + .set_bw(LoRaBandwidth::Bw125) + .set_cr(CodingRate::Cr45) + .set_ldro_en(true) + .set_sf(SpreadingFactor::Sf7); + +// configuration for +10 dBm output power +// see table 35 "PA optimal setting and operating modes" +const PA_CONFIG: PaConfig = PaConfig::new() + .set_pa_duty_cycle(0x1) + .set_hp_max(0x0) + .set_pa(PaSel::Lp); + +const TCXO_MODE: TcxoMode = TcxoMode::new() + .set_txco_trim(TcxoTrim::Volts1pt7) + .set_timeout(Timeout::from_duration_sat( + core::time::Duration::from_millis(10), + )); + +const TX_PARAMS: TxParams = TxParams::new() + .set_power(0x0D) + .set_ramp_time(RampTime::Micros40); + +fn config() -> embassy_stm32::Config { + let mut config = embassy_stm32::Config::default(); + config.rcc = config.rcc.clock_src(embassy_stm32::rcc::ClockSrc::HSE32); + config +} + +#[embassy::main(config = "config()")] +async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) { + unsafe { + Dbgmcu::enable_all(); + } + + let mut led1 = Output::new(p.PB15, Level::High, Speed::Low); + let mut led2 = Output::new(p.PB9, Level::Low, Speed::Low); + let mut led3 = Output::new(p.PB11, Level::Low, Speed::Low); + + let button = Input::new(p.PA0, Pull::Up); + let mut pin = ExtiInput::new(button, p.EXTI0); + + let mut radio_irq = interrupt::take!(SUBGHZ_RADIO); + let mut radio = SubGhz::new(p.SUBGHZSPI, p.PA5, p.PA7, p.PA6, NoDma, NoDma); + + defmt::info!("Radio ready for use"); + + unwrap!(led1.set_low()); + + unwrap!(led2.set_high()); + + unwrap!(radio.set_standby(StandbyClk::Rc)); + unwrap!(radio.set_tcxo_mode(&TCXO_MODE)); + unwrap!(radio.set_standby(StandbyClk::Hse)); + unwrap!(radio.set_regulator_mode(RegMode::Ldo)); + unwrap!(radio.set_buffer_base_address(TX_BUF_OFFSET, RX_BUF_OFFSET)); + unwrap!(radio.set_pa_config(&PA_CONFIG)); + unwrap!(radio.set_pa_ocp(Ocp::Max60m)); + unwrap!(radio.set_tx_params(&TX_PARAMS)); + unwrap!(radio.set_packet_type(PacketType::LoRa)); + unwrap!(radio.set_lora_sync_word(LoRaSyncWord::Public)); + unwrap!(radio.set_lora_mod_params(&LORA_MOD_PARAMS)); + unwrap!(radio.set_lora_packet_params(&LORA_PACKET_PARAMS)); + unwrap!(radio.calibrate_image(CalibrateImage::ISM_863_870)); + unwrap!(radio.set_rf_frequency(&RF_FREQ)); + + defmt::info!("Status: {:?}", unwrap!(radio.status())); + + unwrap!(led2.set_low()); + + loop { + pin.wait_for_rising_edge().await; + unwrap!(led3.set_high()); + unwrap!(radio.set_irq_cfg(&CfgIrq::new().irq_enable_all(Irq::TxDone))); + unwrap!(radio.write_buffer(TX_BUF_OFFSET, PING_DATA_BYTES)); + unwrap!(radio.set_tx(Timeout::DISABLED)); + + InterruptFuture::new(&mut radio_irq).await; + let (_, irq_status) = unwrap!(radio.irq_status()); + if irq_status & Irq::TxDone.mask() != 0 { + defmt::info!("TX done"); + } + unwrap!(radio.clear_irq_status(irq_status)); + unwrap!(led3.set_low()); + } +} -- cgit