kxcj9 0.2.0

Platform-agnostic Rust driver for the KXCJ9 ultra-low-power tri-axis accelerometer.
Documentation
extern crate embedded_hal_mock as hal;
extern crate kxcj9;
use hal::i2c::Transaction as I2cTrans;
use kxcj9::{
    InterruptInfo, InterruptPinLatching as IPL, InterruptPinPolarity as IPPOL,
    WakeUpInterruptConfig, WakeUpOutputDataRate, WakeUpTriggerMotion,
};

mod common;
use common::{destroy, new_1008, new_1018, BitFlags as BF, Register as Reg, DEV_ADDR};

#[test]
fn interrupt_has_happened() {
    let transactions = [I2cTrans::write_read(
        DEV_ADDR,
        vec![Reg::STATUS],
        vec![BF::INT],
    )];
    let mut sensor = new_1018(&transactions);
    assert!(sensor.has_interrupt_happened().unwrap());
    destroy(sensor);
}

#[test]
fn interrupt_has_not_happened() {
    let transactions = [I2cTrans::write_read(DEV_ADDR, vec![Reg::STATUS], vec![0])];
    let mut sensor = new_1018(&transactions);
    assert!(!sensor.has_interrupt_happened().unwrap());
    destroy(sensor);
}

#[test]
fn can_clear_interrupts() {
    let transactions = [I2cTrans::write_read(DEV_ADDR, vec![Reg::INT_REL], vec![0])];
    let mut sensor = new_1018(&transactions);
    sensor.clear_interrupts().unwrap();
    destroy(sensor);
}

#[test]
fn no_interrupt_has_happened() {
    let transactions = [I2cTrans::write_read(
        DEV_ADDR,
        vec![Reg::INT_SOURCE1],
        vec![0, 0],
    )];
    let mut sensor = new_1018(&transactions);
    assert_eq!(
        InterruptInfo::default(),
        sensor.read_interrupt_info().unwrap()
    );
    destroy(sensor);
}

macro_rules! int_happened_test {
    ($name:ident, $int_source1:expr, $int_source2:expr, $field:ident) => {
        #[test]
        fn $name() {
            let transactions = [I2cTrans::write_read(
                DEV_ADDR,
                vec![Reg::INT_SOURCE1],
                vec![$int_source1, $int_source2],
            )];
            let mut sensor = new_1018(&transactions);
            assert_eq!(
                InterruptInfo {
                    $field: true,
                    ..Default::default()
                },
                sensor.read_interrupt_info().unwrap()
            );
            destroy(sensor);
        }
    };
}
int_happened_test!(data_ready_int, BF::DRDY, 0, data_ready);
int_happened_test!(wake_up_int, BF::WUFS, 0, wake_up);
int_happened_test!(wake_up_x_pos_int, 0, BF::XPWU, wake_up_x_positive);
int_happened_test!(wake_up_x_neg_int, 0, BF::XNWU, wake_up_x_negative);
int_happened_test!(wake_up_y_pos_int, 0, BF::YPWU, wake_up_y_positive);
int_happened_test!(wake_up_y_neg_int, 0, BF::YNWU, wake_up_y_negative);
int_happened_test!(wake_up_z_pos_int, 0, BF::ZPWU, wake_up_z_positive);
int_happened_test!(wake_up_z_neg_int, 0, BF::ZNWU, wake_up_z_negative);

macro_rules! change_ctrl1_test {
    ($name:ident, $method:ident, $ctrl1_after:expr) => {
        #[test]
        fn $name() {
            let transactions = [
                I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, 0]),
                I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, $ctrl1_after]),
            ];
            let mut sensor = new_1018(&transactions);
            sensor.$method().unwrap();
            destroy(sensor);
        }
    };
}

change_ctrl1_test!(can_enable_drdy_int, enable_data_ready_interrupt, BF::DRDYE);
change_ctrl1_test!(can_disable_drdy_int, disable_data_ready_interrupt, 0);
change_ctrl1_test!(can_disable_wake_up_int, disable_wake_up_interrupt, 0);

#[test]
fn can_enable_wake_up_int() {
    let transactions = [
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, 0]),
        I2cTrans::write(DEV_ADDR, vec![Reg::INT_CTRL2, 0b0011_1111]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL2, 0]),
        I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_TIMER, 1]),
        I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_THRESHOLD, 8]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1 | BF::WUFE]),
    ];
    let mut sensor = new_1008(&transactions);
    sensor.enable().unwrap();
    let config = WakeUpInterruptConfig::default();
    sensor.enable_wake_up_interrupt(config).unwrap();
    destroy(sensor);
}

#[test]
fn enable_wu_int_disable_all() {
    let transactions = [
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, 0]),
        I2cTrans::write(DEV_ADDR, vec![Reg::INT_CTRL2, 0]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL2, 0]),
        I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_TIMER, 1]),
        I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_THRESHOLD, 8]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1 | BF::WUFE]),
    ];
    let mut sensor = new_1008(&transactions);
    sensor.enable().unwrap();
    let trigger_motion = WakeUpTriggerMotion {
        x_negative: false,
        x_positive: false,
        y_negative: false,
        y_positive: false,
        z_negative: false,
        z_positive: false,
    };
    let config = WakeUpInterruptConfig {
        trigger_motion,
        ..Default::default()
    };
    sensor.enable_wake_up_interrupt(config).unwrap();
    destroy(sensor);
}

#[test]
fn enable_wu_int_enable_all() {
    let transactions = [
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, 0]),
        I2cTrans::write(DEV_ADDR, vec![Reg::INT_CTRL2, 0b0011_1111]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL2, 0]),
        I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_TIMER, 1]),
        I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_THRESHOLD, 8]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1 | BF::WUFE]),
    ];
    let mut sensor = new_1008(&transactions);
    sensor.enable().unwrap();
    let trigger_motion = WakeUpTriggerMotion::default();
    let config = WakeUpInterruptConfig {
        trigger_motion,
        ..Default::default()
    };
    sensor.enable_wake_up_interrupt(config).unwrap();
    destroy(sensor);
}

macro_rules! enable_wu_int_motion_test {
    ($name:ident, $direction:ident, $int_ctrl2:expr) => {
        #[test]
        fn $name() {
            let transactions = [
                I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1]),
                I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, 0]),
                I2cTrans::write(DEV_ADDR, vec![Reg::INT_CTRL2, $int_ctrl2]),
                I2cTrans::write(DEV_ADDR, vec![Reg::CTRL2, 0]),
                I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_TIMER, 1]),
                I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_THRESHOLD, 8]),
                I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1 | BF::WUFE]),
            ];
            let mut sensor = new_1008(&transactions);
            sensor.enable().unwrap();
            let mut trigger_motion = WakeUpTriggerMotion {
                x_negative: false,
                x_positive: false,
                y_negative: false,
                y_positive: false,
                z_negative: false,
                z_positive: false,
            };
            trigger_motion.$direction = true;
            let config = WakeUpInterruptConfig {
                trigger_motion,
                ..Default::default()
            };
            sensor.enable_wake_up_interrupt(config).unwrap();
            destroy(sensor);
        }
    };
}

enable_wu_int_motion_test!(can_enable_wu_int_motion_x_neg, x_negative, BF::XNWU);
enable_wu_int_motion_test!(can_enable_wu_int_motion_x_pos, x_positive, BF::XPWU);
enable_wu_int_motion_test!(can_enable_wu_int_motion_y_neg, y_negative, BF::YNWU);
enable_wu_int_motion_test!(can_enable_wu_int_motion_y_pos, y_positive, BF::YPWU);
enable_wu_int_motion_test!(can_enable_wu_int_motion_z_neg, z_negative, BF::ZNWU);
enable_wu_int_motion_test!(can_enable_wu_int_motion_z_pos, z_positive, BF::ZPWU);

macro_rules! set_wu_odr_test {
    ($name:ident, $variant:ident, $ctrl2:expr) => {
        #[test]
        fn $name() {
            let transactions = [
                I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1]),
                I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, 0]),
                I2cTrans::write(DEV_ADDR, vec![Reg::INT_CTRL2, 0b0011_1111]),
                I2cTrans::write(DEV_ADDR, vec![Reg::CTRL2, $ctrl2]),
                I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_TIMER, 1]),
                I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_THRESHOLD, 8]),
                I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1 | BF::WUFE]),
            ];
            let mut sensor = new_1008(&transactions);
            sensor.enable().unwrap();
            let data_rate = WakeUpOutputDataRate::$variant;
            let config = WakeUpInterruptConfig {
                data_rate,
                ..Default::default()
            };
            sensor.enable_wake_up_interrupt(config).unwrap();
            destroy(sensor);
        }
    };
}

set_wu_odr_test!(set_odr_hz0, Hz0_781, 0);
set_wu_odr_test!(set_odr_hz1, Hz1_563, 1);
set_wu_odr_test!(set_odr_hz3, Hz3_125, 2);
set_wu_odr_test!(set_odr_hz6, Hz6_25, 3);
set_wu_odr_test!(set_odr_hz12, Hz12_5, 4);
set_wu_odr_test!(set_odr_hz25, Hz25, 5);
set_wu_odr_test!(set_odr_hz50, Hz50, 6);
set_wu_odr_test!(set_odr_hz100, Hz100, 7);

#[test]
fn can_set_wake_up_fault_count() {
    let transactions = [
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, 0]),
        I2cTrans::write(DEV_ADDR, vec![Reg::INT_CTRL2, 0b0011_1111]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL2, 0]),
        I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_TIMER, 0xAB]),
        I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_THRESHOLD, 8]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1 | BF::WUFE]),
    ];
    let mut sensor = new_1008(&transactions);
    sensor.enable().unwrap();
    let config = WakeUpInterruptConfig {
        fault_count: 0xAB,
        ..Default::default()
    };
    sensor.enable_wake_up_interrupt(config).unwrap();
    destroy(sensor);
}

#[test]
fn cannot_set_wake_up_fault_count_0() {
    let mut sensor = new_1018(&[]);
    let config = WakeUpInterruptConfig {
        fault_count: 0,
        ..Default::default()
    };
    sensor
        .enable_wake_up_interrupt(config)
        .expect_err("Should return error");
    destroy(sensor);
}

macro_rules! wrong_th_test {
    ($name:ident, $create:ident, $threshold:expr) => {
        #[test]
        fn $name() {
            let mut sensor = $create(&[]);
            let config = WakeUpInterruptConfig {
                threshold: $threshold,
                ..Default::default()
            };
            sensor
                .enable_wake_up_interrupt(config)
                .expect_err("Should return error");
            destroy(sensor);
        }
    };
}

wrong_th_test!(cannot_set_wake_up_th_too_low_1018, new_1018, -0.1);
wrong_th_test!(cannot_set_wake_up_th_too_high_1018, new_1018, 16.1);
wrong_th_test!(cannot_set_wake_up_th_too_low_1008, new_1008, -0.1);
wrong_th_test!(cannot_set_wake_up_th_too_high_1008, new_1008, 8.1);

#[test]
fn can_set_wake_up_th_2() {
    let transactions = [
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, 0]),
        I2cTrans::write(DEV_ADDR, vec![Reg::INT_CTRL2, 0b0011_1111]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL2, 0]),
        I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_TIMER, 1]),
        I2cTrans::write(DEV_ADDR, vec![Reg::WAKEUP_THRESHOLD, 32]),
        I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1 | BF::WUFE]),
    ];
    let mut sensor = new_1008(&transactions);
    sensor.enable().unwrap();
    let config = WakeUpInterruptConfig {
        threshold: 2.0,
        ..Default::default()
    };
    sensor.enable_wake_up_interrupt(config).unwrap();
    destroy(sensor);
}

macro_rules! int_pin_test {
    ($name:ident, $method:ident, $int_ctrl1:expr $(, $arg:expr)*) => {
        #[test]
        fn $name() {
            let transactions = [
                I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1]),
                I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, 0]),
                I2cTrans::write(DEV_ADDR, vec![Reg::INT_CTRL1, $int_ctrl1]),
                I2cTrans::write(DEV_ADDR, vec![Reg::CTRL1, BF::PC1]),
            ];
            let mut sensor = new_1018(&transactions);
            sensor.enable().unwrap();
            sensor.$method($($arg)*).unwrap();
            destroy(sensor);
        }
    };
}

int_pin_test!(can_enable_int_pin, enable_interrupt_pin, BF::IEN | BF::IEA);
int_pin_test!(can_disable_int_pin, disable_interrupt_pin, BF::IEA);
int_pin_test!(
    can_set_int_pin_polarity_ah,
    set_interrupt_pin_polarity,
    BF::IEA,
    IPPOL::ActiveHigh
);
int_pin_test!(
    can_set_int_pin_polarity_al,
    set_interrupt_pin_polarity,
    0,
    IPPOL::ActiveLow
);
int_pin_test!(
    can_set_int_pin_latching,
    set_interrupt_pin_latching,
    BF::IEA,
    IPL::Latching
);
int_pin_test!(
    can_set_int_pin_non_latching,
    set_interrupt_pin_latching,
    BF::IEA | BF::IEL,
    IPL::NonLatching
);