This commit is contained in:
2025-06-03 13:57:39 +02:00
parent 55ed793c12
commit 1188995b80
11 changed files with 296 additions and 263 deletions

BIN
bluetooth_demo/.DS_Store vendored Normal file

Binary file not shown.

36
bluetooth_demo/Cargo.toml Normal file
View File

@@ -0,0 +1,36 @@
[package]
name = "bluetooth_demo"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cortex-m.workspace = true
cortex-m-rt.workspace = true
nrf52840-hal.workspace = true
usb-device.workspace = true
usbd-serial.workspace = true
libm.workspace = true
# nalgebra.workspace = true
heapless.workspace = true
lsm6ds3tr.workspace = true
defmt.workspace = true
defmt-rtt.workspace = true
embedded-alloc.workspace = true
embedded-hal.workspace = true
embedded-hal-async.workspace = true
nrf-softdevice.workspace = true
nrf-softdevice-s140.workspace = true
embassy-nrf.workspace = true
embassy-time.workspace = true
embassy-executor.workspace = true
embassy-sync.workspace = true
embassy-embedded-hal.workspace = true
fixed.workspace = true
atomic-pool.workspace = true
static_cell.workspace = true
embassy-usb.workspace = true
embassy-futures.workspace = true
panic-probe.workspace = true
assign-resources.workspace = true

View File

@@ -0,0 +1,4 @@
#![macro_use]
use defmt_rtt as _; // global logger
use embassy_nrf as _; // time driver

View File

@@ -0,0 +1,89 @@
#![no_main]
#![no_std]
use assign_resources::assign_resources;
use core::fmt::Write;
use defmt::{dbg, info, unwrap};
use embassy_executor::Spawner;
use embassy_nrf::gpio::{Level, Output, OutputDrive};
use embassy_nrf::usb::vbus_detect::HardwareVbusDetect;
use embassy_nrf::usb::Driver;
use embassy_nrf::{bind_interrupts, peripherals, twim, usb, Peripherals};
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
use embassy_sync::mutex::Mutex;
use embassy_time::{Duration, Timer};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
use embassy_usb::driver::EndpointError;
use embassy_usb::{Builder, Config, UsbDevice};
use heapless::String;
use lsm6ds3tr::interface::I2cInterface;
use lsm6ds3tr::{
registers::{GyroSampleRate, GyroScale},
AccelSampleRate, AccelScale, AccelSettings, GyroSettings, LsmSettings, LSM6DS3TR,
};
use static_cell::StaticCell;
use {defmt_rtt as _, panic_probe as _};
mod usb_dfu;
bind_interrupts!(struct Irqs {
USBD => usb::InterruptHandler<peripherals::USBD>;
CLOCK_POWER => usb::vbus_detect::InterruptHandler;
});
assign_resources! {
imu: ImuResources {
spi: SPI2,
}
}
bind_interrupts!(struct IrqsTest {
// SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler<TWISPI0>;
TWISPI0 => twim::InterruptHandler<peripherals::TWISPI0>;
});
static I2C_BUS: StaticCell<Mutex<ThreadModeRawMutex, twim::Twim<peripherals::TWISPI0>>> =
StaticCell::new();
type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>;
#[embassy_executor::main]
async fn main(spawner: Spawner) {
let p = embassy_nrf::init(Default::default());
// let resources = split_resources!(p);
// Create the driver, from the HAL.
let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs));
let mut config = Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("Umbrella Corporation");
config.product = Some("Secret Project");
config.serial_number = Some("11880");
config.max_power = 100;
config.max_packet_size_0 = 64;
static STATE: StaticCell<State> = StaticCell::new();
let state = STATE.init(State::new());
// Create embassy-usb DeviceBuilder using the driver and config.
static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new();
static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new();
static MSOS_DESC: StaticCell<[u8; 128]> = StaticCell::new();
static CONTROL_BUF: StaticCell<[u8; 128]> = StaticCell::new();
let mut builder = Builder::new(
driver,
config,
&mut CONFIG_DESC.init([0; 256])[..],
&mut BOS_DESC.init([0; 256])[..],
&mut MSOS_DESC.init([0; 128])[..],
&mut CONTROL_BUF.init([0; 128])[..],
);
// Create classes on the builder.
let mut class = CdcAcmClass::new(&mut builder, state, 64);
// Build the builder.
let usb = builder.build();
unwrap!(spawner.spawn(usb_task(usb)));
}

View File

@@ -0,0 +1,157 @@
use crate::Irqs;
use core::fmt::Write;
use defmt::{info, unwrap};
use embassy_executor::Spawner;
use embassy_futures::join::join;
use embassy_nrf::peripherals::USBD;
use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect};
use embassy_nrf::usb::{Driver, Instance};
use embassy_nrf::{pac, peripherals};
use embassy_time::{Duration, Timer};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
use embassy_usb::driver::EndpointError;
use embassy_usb::{Builder, Config};
use heapless::String;
use {defmt_rtt as _, panic_probe as _};
const MAGIC_REBOOT_MESSAGE: &str = "bootloader";
type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>;
/// Creates a usb serial device.
/// Sending it [MAGIC_REBOOT_MESSAGE] will reboot the device
/// into serial-only-dfu mode.
pub fn setup_dfu_over_usb(spawner: &Spawner, usbd: USBD) {
spawner.spawn(dfu_over_usb(usbd, *spawner)).unwrap();
}
#[embassy_executor::task]
async fn dfu_over_usb(usbd: USBD, spawner: Spawner) {
pac::CLOCK.tasks_hfclkstart().write_value(1);
while pac::CLOCK.events_hfclkstarted().read() != 1 {}
// Create the driver, from the HAL.
let driver = Driver::new(usbd, Irqs, HardwareVbusDetect::new(Irqs));
// Create embassy-usb Config
let mut config = Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("Embassy");
config.product = Some("USB-serial example");
config.serial_number = Some("12345678");
config.max_power = 100;
config.max_packet_size_0 = 64;
// Create embassy-usb DeviceBuilder using the driver and config.
// It needs some buffers for building the descriptors.
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
let mut msos_descriptor = [0; 256];
let mut control_buf = [0; 64];
let mut state = State::new();
let mut builder = Builder::new(
driver,
config,
&mut config_descriptor,
&mut bos_descriptor,
&mut msos_descriptor,
&mut control_buf,
);
// Create classes on the builder.
let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
// Build the builder.
let mut usb = builder.build();
// Run the USB device.
let usb_fut = usb.run();
// Do stuff with the class!
// let reboot_fut = async {
// loop {
// class.wait_connection().await;
// let _ = reboot_on_magic_message(&mut class).await;
// }
// };
let print_fut = async {
let mut count = 1;
loop {
class.wait_connection().await;
info!("Connected");
let mut data = String::<32>::new();
if write!(data, "Count: {}\r\n", count).is_ok() {
if let Err(e) = class.write_packet(data.as_bytes()).await {
info!("Failed to write to serial console: {:?}", e);
}
}
count += 1;
// Add a delay of 1 second
Timer::after(Duration::from_secs(1)).await;
info!("Disconnected");
}
};
join(usb_fut, print_fut).await;
}
struct Disconnected {}
impl From<EndpointError> for Disconnected {
fn from(val: EndpointError) -> Self {
match val {
EndpointError::BufferOverflow => panic!("Buffer overflow"),
EndpointError::Disabled => Disconnected {},
}
}
}
async fn reboot_on_magic_message<'d, T: Instance + 'd, P: VbusDetect + 'd>(
class: &mut CdcAcmClass<'d, Driver<'d, T, P>>,
) -> Result<(), Disconnected> {
let mut buf = [0; 64];
loop {
let n = class.read_packet(&mut buf).await?;
let data = &buf[..n];
if data == MAGIC_REBOOT_MESSAGE.as_bytes() {
// Reboot the controller in DFU mode.
// The magic number has been taken from the arduino bootloader:
// https://github.com/mike1808/PIO_SEEED_Adafruit_nRF52_Arduino/blob/master/cores/nRF5/wiring.c#L26
let dfu_magic_serial_only_reset = 0x4E;
pac::POWER
.gpregret()
.write(|w| w.0 = dfu_magic_serial_only_reset);
cortex_m::peripheral::SCB::sys_reset();
}
}
}
#[embassy_executor::task]
async fn write_task(mut class: CdcAcmClass<'static, MyDriver>) {
let mut count = 1;
loop {
class.wait_connection().await;
info!("Connected");
let mut data = String::<32>::new();
if write!(data, "Count: {}\r\n", count).is_ok() {
if let Err(e) = class.write_packet(data.as_bytes()).await {
info!("Failed to write to serial console: {:?}", e);
}
}
count += 1;
// Add a delay of 1 second
Timer::after(Duration::from_secs(1)).await;
info!("Disconnected");
}
}