Skip to content
Snippets Groups Projects
Select Git revision
  • a419d7eef99c917ceebab1d67a9ea1791775d135
  • master default protected
2 results

pmw3389.rs

Blame
  • Per Lindgren's avatar
    Per Lindgren authored
    a419d7ee
    History
    pmw3389.rs 11.64 KiB
    #![deny(unsafe_code)]
    // #![deny(warnings)]
    #![no_main]
    #![no_std]
    
    use cortex_m::{
        iprintln,
        peripheral::{itm::Stim, DWT},
    };
    use embedded_hal::spi::MODE_0;
    // use cortex_m_semihosting::hprintln;
    use panic_halt as _;
    use rtic::cyccnt::{Instant, U32Ext as _};
    use stm32f4xx_hal::{
        // gpio::gpioa::PA0,
        prelude::*,
        // rcc::CFGR,
        spi::Spi,
        stm32,
    };
    
    //use crate::hal::gpio::{gpioa::PA0, Edge, Input, PullDown};
    //use hal::spi::{Mode, Phase, Polarity};
    
    #[rtic::app(device = stm32f4xx_hal::stm32, monotonic = rtic::cyccnt::CYCCNT, peripherals = true)]
    const APP: () = {
        struct Resources {
            // late resources
            GPIOA: stm32::GPIOA,
            ITM: stm32::ITM,
        }
        #[init(schedule = [toggle])]
        fn init(cx: init::Context) -> init::LateResources {
            let mut core = cx.core;
            let device = cx.device;
    
            // Configure led:
            // power on GPIOA, RM0368 6.3.11
            device.RCC.ahb1enr.modify(|_, w| w.gpioaen().set_bit());
            // configure PA5 as output, RM0368 8.4.1
            device.GPIOA.moder.modify(|_, w| w.moder5().bits(1));
    
            // setup clocks
            let rcc = device.RCC.constrain();
    
            let clocks = rcc.cfgr.freeze();
    
            let stim = &mut core.ITM.stim[0];
    
            iprintln!(stim, "pmw3389");
    
            // iprintln!(stim, "hclk {:?}", clocks.hclk().into::());
    
            // Initialize (enable) the monotonic timer (CYCCNT)
            core.DCB.enable_trace();
            // required on Cortex-M7 devices that software lock the DWT (e.g. STM32F7)
            DWT::unlock();
            core.DWT.enable_cycle_counter();
    
            // Configure SPI
            // spi2
            // sck    - pb10, (yellow)
            // miso   - pc2, (red)
            // mosi   - pc3, (orange)
            // ncs    - pb4, (long yellow)
            // motion - (brown)
            //
            // +5, (white)
            // gnd, (black)
    
            let gpiob = device.GPIOB.split();
            let gpioc = device.GPIOC.split();
    
            let sck = gpiob.pb10.into_alternate_af5();
            let miso = gpioc.pc2.into_alternate_af5();
            let mosi = gpioc.pc3.into_alternate_af5();
            let cs = gpiob.pb4.into_push_pull_output();
    
            let spi = Spi::spi2(
                device.SPI2,
                (sck, miso, mosi),
                // Mode {
                //     polarity: Polarity::IdleLow,
                //     phase: Phase::CaptureOnFirstTransition,
                // },
                MODE_0,
                stm32f4xx_hal::time::KiloHertz(2000).into(),
                clocks,
            );
    
            iprintln!(stim, "clocks:\n hclk {}", clocks.hclk().0);
    
            let mut pmw3389 = pmw3389::Pmw3389::new(spi, cs).unwrap();
            let id = pmw3389.product_id().unwrap();
            iprintln!(stim, "id {}", id);
    
            // semantically, the monotonic timer is frozen at time "zero" during `init`
            // NOTE do *not* call `Instant::now` in this context; it will return a nonsense value
            // let now = cx.start; // the start time of the system
    
            // Schedule `toggle` to run 8e6 cycles (clock cycles) in the future
            // cx.schedule.toggle(now + 8_000_000.cycles()).unwrap();
    
            // pass on late resources
            init::LateResources {
                GPIOA: device.GPIOA,
                ITM: core.ITM,
            }
        }
    
        #[task(resources = [GPIOA, ITM], schedule = [toggle])]
        fn toggle(cx: toggle::Context) {
            static mut TOGGLE: bool = false;
            iprintln!(&mut cx.resources.ITM.stim[0], "foo  @ {:?}", Instant::now());
    
            if *TOGGLE {
                cx.resources.GPIOA.bsrr.write(|w| w.bs5().set_bit());
            } else {
                cx.resources.GPIOA.bsrr.write(|w| w.br5().set_bit());
            }
    
            *TOGGLE = !*TOGGLE;
            cx.schedule
                .toggle(cx.scheduled + 8_000_000.cycles())
                .unwrap();
        }
    
        extern "C" {
            fn EXTI0();
        }
    };
    
    mod pmw3389 {
    
        use embedded_hal::blocking::spi::{Transfer, Write};
        use embedded_hal::digital::v2::OutputPin;
        use embedded_hal::spi::Mode;
    
        #[allow(dead_code)]
        #[derive(Clone, Copy)]
        pub enum Register {
            ProductId = 0x00,
            RevisionId = 0x01,
            Motion = 0x02,
            DeltaXL = 0x03,
            DeltaXH = 0x04,
            DeltaYL = 0x05,
            DeltaYH = 0x06,
            SQUAL = 0x07,
            RawDataSum = 0x08,
            MaximumRawdata = 0x09,
            MinimumRawdata = 0x0A,
            ShutterLower = 0x0B,
            ShutterUpper = 0x0C,
            RippleControl = 0x0D,
            ResoultionL = 0x0E,
            ResolutionH = 0x0F,
            Config2 = 0x10,
            AngleTune = 0x11,
            FrameCapture = 0x12,
            SROMEnable = 0x13,
            RunDownshift = 0x14,
            Rest1RateLower = 0x15,
            Rest1RateUpper = 0x16,
            Rest1Downshift = 0x17,
            Rest2RateLower = 0x18,
            Rest2RateUpper = 0x19,
            Rest2Downshift = 0x1A,
            Rest3RateLower = 0x1B,
            Rest3RateUpper = 0x1C,
            Observation = 0x24,
            DataOutLower = 0x25,
            DataOutUpper = 0x26,
            RawDataDump = 0x29,
            SROMId = 0x2A,
            MinSQRun = 0x2B,
            RawDataThreshold = 0x2C,
            Control2 = 0x2D,
            Config5L = 0x2E,
            Config5H = 0x2F,
            PowerUpReset = 0x3A,
            Shutdown = 0x3B,
            InverseProductID = 0x3F,
            LiftCutoffTune3 = 0x41,
            AngleSnap = 0x42,
            LiftCutoffTune1 = 0x4A,
            MotionBurst = 0x50,
            LiftCutoffTune1Timeout = 0x58,
            LiftCutoffTune1MinLength = 0x5A,
            SROMLoadBurst = 0x62,
            LiftConfig = 0x63,
            RawDataBurst = 0x64,
            LiftCutoffTune2 = 0x65,
            LiftCutoffTune2Timeout = 0x71,
            LiftCutoffTune2MinLength = 0x72,
            PWMPeriodCnt = 0x73,
            PWMWidthCnt = 0x74,
        }
    
        impl Register {
            fn addr(self) -> u8 {
                self as u8
            }
        }
    
        const READ: u8 = 1 << 7;
        const WRITE: u8 = 0 << 7;
        const MULTI: u8 = 1 << 6;
        const SINGLE: u8 = 0 << 6;
    
        pub struct Pmw3389<SPI, CS> {
            spi: SPI,
            cs: CS,
        }
    
        impl<SPI, CS, E> Pmw3389<SPI, CS>
        where
            SPI: Transfer<u8, Error = E> + Write<u8, Error = E>,
            CS: OutputPin,
        {
            /// Creates a new driver from a SPI peripheral and a NCS pin
            pub fn new(spi: SPI, cs: CS) -> Result<Self, E> {
                let mut pmw3389 = Pmw3389 { spi, cs };
    
                // power up and enable all the axes
                // l3gd20.write_register(Register::CTRL_REG1, 0b00_00_1_111)?;
    
                Ok(pmw3389)
            }
    
            /// Reads the ProductId register; should return `0x47`
            pub fn product_id(&mut self) -> Result<u8, E> {
                self.read_register(Register::ProductId)
            }
    
            // /// Temperature measurement + gyroscope measurements
            // pub fn all(&mut self) -> Result<Measurements, E> {
            //     let mut bytes = [0u8; 9];
            //     self.read_many(Register::OUT_TEMP, &mut bytes)?;
    
            //     Ok(Measurements {
            //         gyro: I16x3 {
            //             x: (bytes[3] as u16 + ((bytes[4] as u16) << 8)) as i16,
            //             y: (bytes[5] as u16 + ((bytes[6] as u16) << 8)) as i16,
            //             z: (bytes[7] as u16 + ((bytes[8] as u16) << 8)) as i16,
            //         },
            //         temp: bytes[1] as i8,
            //     })
            // }
    
            // /// Gyroscope measurements
            // pub fn gyro(&mut self) -> Result<I16x3, E> {
            //     let mut bytes = [0u8; 7];
            //     self.read_many(Register::OUT_X_L, &mut bytes)?;
    
            //     Ok(I16x3 {
            //         x: (bytes[1] as u16 + ((bytes[2] as u16) << 8)) as i16,
            //         y: (bytes[3] as u16 + ((bytes[4] as u16) << 8)) as i16,
            //         z: (bytes[5] as u16 + ((bytes[6] as u16) << 8)) as i16,
            //     })
            // }
    
            // /// Temperature sensor measurement
            // pub fn temp(&mut self) -> Result<i8, E> {
            //     Ok(self.read_register(Register::OUT_TEMP)? as i8)
            // }
    
            // /// Read `STATUS_REG` of sensor
            // pub fn status(&mut self) -> Result<Status, E> {
            //     let sts = self.read_register(Register::STATUS_REG)?;
            //     Ok(Status::from_u8(sts))
            // }
    
            // /// Get the current Output Data Rate
            // pub fn odr(&mut self) -> Result<Odr, E> {
            //     // Read control register
            //     let reg1 = self.read_register(Register::CTRL_REG1)?;
            //     Ok(Odr::from_u8(reg1))
            // }
    
            // /// Set the Output Data Rate
            // pub fn set_odr(&mut self, odr: Odr) -> Result<&mut Self, E> {
            //     self.change_config(Register::CTRL_REG1, odr)
            // }
    
            // /// Get current Bandwidth
            // pub fn bandwidth(&mut self) -> Result<Bandwidth, E> {
            //     let reg1 = self.read_register(Register::CTRL_REG1)?;
            //     Ok(Bandwidth::from_u8(reg1))
            // }
    
            // /// Set low-pass cut-off frequency (i.e. bandwidth)
            // ///
            // /// See `Bandwidth` for further explanation
            // pub fn set_bandwidth(&mut self, bw: Bandwidth) -> Result<&mut Self, E> {
            //     self.change_config(Register::CTRL_REG1, bw)
            // }
    
            // /// Get the current Full Scale Selection
            // ///
            // /// This is the sensitivity of the sensor, see `Scale` for more information
            // pub fn scale(&mut self) -> Result<Scale, E> {
            //     let scl = self.read_register(Register::CTRL_REG4)?;
            //     Ok(Scale::from_u8(scl))
            // }
    
            // /// Set the Full Scale Selection
            // ///
            // /// This sets the sensitivity of the sensor, see `Scale` for more
            // /// information
            // pub fn set_scale(&mut self, scale: Scale) -> Result<&mut Self, E> {
            //     self.change_config(Register::CTRL_REG4, scale)
            // }
    
            fn read_register(&mut self, reg: Register) -> Result<u8, E> {
                let _ = self.cs.set_low();
    
                let mut buffer = [reg.addr() | SINGLE | READ, 0];
                self.spi.transfer(&mut buffer)?;
    
                let _ = self.cs.set_high();
    
                Ok(buffer[1])
            }
    
            /// Read multiple bytes starting from the `start_reg` register.
            /// This function will attempt to fill the provided buffer.
            fn read_many(&mut self, start_reg: Register, buffer: &mut [u8]) -> Result<(), E> {
                let _ = self.cs.set_low();
                buffer[0] = start_reg.addr() | MULTI | READ;
                self.spi.transfer(buffer)?;
                let _ = self.cs.set_high();
    
                Ok(())
            }
    
            fn write_register(&mut self, reg: Register, byte: u8) -> Result<(), E> {
                let _ = self.cs.set_low();
    
                let buffer = [reg.addr() | SINGLE | WRITE, byte];
                self.spi.write(&buffer)?;
    
                let _ = self.cs.set_high();
    
                Ok(())
            }
    
            // /// Change configuration in register
            // ///
            // /// Helper function to update a particular part of a register without
            // /// affecting other parts of the register that might contain desired
            // /// configuration. This allows the `L3gd20` struct to be used like
            // /// a builder interface when configuring specific parameters.
            // fn change_config<B: BitValue>(&mut self, reg: Register, bits: B) -> Result<&mut Self, E> {
            //     // Create bit mask from width and shift of value
            //     let mask = B::mask() << B::shift();
            //     // Extract the value as u8
            //     let bits = (bits.value() << B::shift()) & mask;
            //     // Read current value of register
            //     let current = self.read_register(reg)?;
            //     // Use supplied mask so we don't affect more than necessary
            //     let masked = current & !mask;
            //     // Use `or` to apply the new value without affecting other parts
            //     let new_reg = masked | bits;
            //     self.write_register(reg, new_reg)?;
            //     Ok(self)
            // }
        }
    }