|
| 1 | +#![no_std] |
| 2 | +#![no_main] |
| 3 | + |
| 4 | +use defmt::{error, info, warn}; |
| 5 | +use defmt_rtt as _; |
| 6 | +use embassy_executor::Spawner; |
| 7 | +use embassy_stm32::{ |
| 8 | + gpio::{Level, Output, Speed}, |
| 9 | + spi::{Config, Mode, Phase, Polarity, Spi}, |
| 10 | + time::Hertz, |
| 11 | +}; |
| 12 | +use embassy_time::{Duration, Timer}; |
| 13 | +use panic_probe as _; |
| 14 | + |
| 15 | +/// MPU6000 Registers & Constants |
| 16 | +const MPU_WHO_AM_I: u8 = 0x75; |
| 17 | +const MPU_WHO_AM_I_VALUE: u8 = 0x68; // Expected value for MPU-6000 |
| 18 | +const MPU_USER_CTRL: u8 = 0x6A; |
| 19 | +const MPU_PWR_MGMT_1: u8 = 0x6B; |
| 20 | +const MPU_ACCEL_XOUT_H: u8 = 0x3B; |
| 21 | +const MPU_READ: u8 = 0x80; |
| 22 | + |
| 23 | +#[embassy_executor::main] |
| 24 | +async fn main(_spawner: Spawner) { |
| 25 | + let peripherals = embassy_stm32::init(Default::default()); |
| 26 | + info!("Device started"); |
| 27 | + |
| 28 | + // Create the SPI bus configuration |
| 29 | + let mut config = Config::default(); |
| 30 | + config.frequency = Hertz(1_000_000); |
| 31 | + |
| 32 | + // MPU6000 requires SPI Mode 3 (CPOL=1, CPHA=1) |
| 33 | + config.mode = Mode { |
| 34 | + polarity: Polarity::IdleHigh, |
| 35 | + phase: Phase::CaptureOnSecondTransition, |
| 36 | + }; |
| 37 | + |
| 38 | + let mut spi = Spi::new( |
| 39 | + peripherals.SPI1, |
| 40 | + peripherals.PA5, // SCK |
| 41 | + peripherals.PA7, // MOSI |
| 42 | + peripherals.PA6, // MISO |
| 43 | + peripherals.GPDMA1_CH0, |
| 44 | + peripherals.GPDMA1_CH1, |
| 45 | + config, |
| 46 | + ); |
| 47 | + |
| 48 | + // We use the PA8 pin as CS |
| 49 | + let mut cs = Output::new(peripherals.PA8, Level::High, Speed::VeryHigh); |
| 50 | + |
| 51 | + // Disable I2C interface |
| 52 | + let disable_i2c_buf: [u8; 2] = [MPU_USER_CTRL, 0x10]; |
| 53 | + cs.set_low(); |
| 54 | + if let Err(e) = spi.write(&disable_i2c_buf).await { |
| 55 | + error!("Failed to disable I2C interface: {:?}", e); |
| 56 | + } |
| 57 | + cs.set_high(); |
| 58 | + |
| 59 | + // Wake up MPU6000 |
| 60 | + let wake_buf: [u8; 2] = [MPU_PWR_MGMT_1, 0x01]; |
| 61 | + cs.set_low(); |
| 62 | + if let Err(e) = spi.write(&wake_buf).await { |
| 63 | + error!("Failed to wake up MPU-6000: {:?}", e); |
| 64 | + } |
| 65 | + cs.set_high(); |
| 66 | + |
| 67 | + // Yield execution to the executor for 100ms to allow the sensor to stabilize |
| 68 | + Timer::after(Duration::from_millis(100)).await; |
| 69 | + |
| 70 | + // Verify WHO_AM_I |
| 71 | + let command = [MPU_WHO_AM_I | MPU_READ, 0x00]; |
| 72 | + let mut rx = [0u8; 2]; |
| 73 | + |
| 74 | + cs.set_low(); |
| 75 | + let res = spi.transfer(&mut rx, &command).await; |
| 76 | + cs.set_high(); |
| 77 | + |
| 78 | + match res { |
| 79 | + Err(e) => { |
| 80 | + error!("SPI transfer failed during WHO_AM_I check: {:?}", e); |
| 81 | + } |
| 82 | + Ok(_) => { |
| 83 | + let who_am_i_value = rx[1]; |
| 84 | + info!("Sensor's WHO_AM_I register is 0x{:02X}", who_am_i_value); |
| 85 | + |
| 86 | + if who_am_i_value == MPU_WHO_AM_I_VALUE { |
| 87 | + info!("Sensor is MPU-6000"); |
| 88 | + } else { |
| 89 | + warn!("This is not an MPU-6000 sensor. Expected 0x68."); |
| 90 | + } |
| 91 | + } |
| 92 | + } |
| 93 | + |
| 94 | + // Scaling factors matching the default configured ranges |
| 95 | + // ±2g -> 16384 LSB/g -> multiply by (9.81 / 16384.0) for m/s² |
| 96 | + // ±250°/s -> 131 LSB/°/s -> multiply by (250.0 / 32768.0) for °/s |
| 97 | + let accel_scale: f32 = 9.81 / 16384.0; |
| 98 | + let gyro_scale: f32 = 250.0 / 32768.0; |
| 99 | + |
| 100 | + // Continuous Data Reading Loop |
| 101 | + loop { |
| 102 | + let mut tx_buf = [0u8; 15]; |
| 103 | + let mut rx_buf = [0u8; 15]; |
| 104 | + tx_buf[0] = MPU_ACCEL_XOUT_H | MPU_READ; |
| 105 | + |
| 106 | + cs.set_low(); |
| 107 | + let transfer_result = spi.transfer(&mut rx_buf, &tx_buf).await; |
| 108 | + cs.set_high(); |
| 109 | + |
| 110 | + match transfer_result { |
| 111 | + Ok(_) => { |
| 112 | + // Parse raw 16-bit signed values (big-endian) skipping the first dummy byte |
| 113 | + let ax_raw = i16::from_be_bytes([rx_buf[1], rx_buf[2]]) as f32; |
| 114 | + let ay_raw = i16::from_be_bytes([rx_buf[3], rx_buf[4]]) as f32; |
| 115 | + let az_raw = i16::from_be_bytes([rx_buf[5], rx_buf[6]]) as f32; |
| 116 | + |
| 117 | + let gx_raw = i16::from_be_bytes([rx_buf[9], rx_buf[10]]) as f32; |
| 118 | + let gy_raw = i16::from_be_bytes([rx_buf[11], rx_buf[12]]) as f32; |
| 119 | + let gz_raw = i16::from_be_bytes([rx_buf[13], rx_buf[14]]) as f32; |
| 120 | + |
| 121 | + // Apply scaling |
| 122 | + let ax = ax_raw * accel_scale; |
| 123 | + let ay = ay_raw * accel_scale; |
| 124 | + let az = az_raw * accel_scale; |
| 125 | + let gx = gx_raw * gyro_scale; |
| 126 | + let gy = gy_raw * gyro_scale; |
| 127 | + let gz = gz_raw * gyro_scale; |
| 128 | + |
| 129 | + info!("Accel (m/s²): X={}, Y={}, Z={}", ax, ay, az); |
| 130 | + info!("Gyro (°/s): X={}, Y={}, Z={}", gx, gy, gz); |
| 131 | + } |
| 132 | + Err(e) => { |
| 133 | + // If a read fails, we just log it and the loop will try again after the delay |
| 134 | + warn!("Failed to read from sensor: {:?}", e); |
| 135 | + } |
| 136 | + } |
| 137 | + |
| 138 | + Timer::after(Duration::from_millis(100)).await; |
| 139 | + } |
| 140 | +} |
0 commit comments