si4703 0.1.0

Platform-agnostic Rust driver for the Si4703 and Si4702 FM radio turners (receivers).
Documentation
use embedded_hal_mock::i2c::{Mock as I2cMock, Transaction as I2cTrans};
use si4703::{
    Band, ChannelSpacing as Spacing, DeEmphasis, Gpio1Config, Gpio2Config, Gpio3Config, OutputMode,
    Si4703, SoftmuteAttenuation, SoftmuteRate, StereoToMonoBlendLevel as Blend, Volume,
};

mod common;
use self::common::{destroy, new_si4703, BitFlags as BF, DEV_ADDR};

#[test]
fn can_create_and_destroy_si4702() {
    let dev = Si4703::new_si4702(I2cMock::new(&[]));
    destroy(dev);
}

write_test!(can_enable_osc, BF::XOSCEN, 16, 6, enable_oscillator);
write_test!(can_enable_ahiz, BF::AHIZEN, 16, 6, enable_audio_high_z);
write_test!(can_disable_ahiz, 0, 16, 6, disable_audio_high_z);

write_powercfg_test!(can_enable, BF::ENABLE, enable);
write_powercfg_test!(can_disable, BF::ENABLE | BF::DISABLE, disable);

write_powercfg_test!(can_unmute, BF::DMUTE, unmute);
write_powercfg_test!(can_mute, 0x0, mute);

write_powercfg_test!(can_dis_smute, BF::DSMUTE, disable_softmute);

macro_rules! en_smute_test {
    ($name:ident, $sysconfig3:expr, $rate:ident, $attenuation:ident) => {
        write_test!(
            $name,
            16,
            0,
            0,
            4,
            $sysconfig3,
            enable_softmute,
            SoftmuteRate::$rate,
            SoftmuteAttenuation::$attenuation
        );
    };
}

en_smute_test!(en_smute_rate_fastest, 0, Fastest, Db16);
en_smute_test!(en_smute_rate_fast, 1 << 14, Fast, Db16);
en_smute_test!(en_smute_rate_slow, 2 << 14, Slow, Db16);
en_smute_test!(en_smute_rate_slowest, 3 << 14, Slowest, Db16);
en_smute_test!(en_smute_att_db14, 1 << 12, Fastest, Db14);
en_smute_test!(en_smute_att_db12, 2 << 12, Fastest, Db12);
en_smute_test!(en_smute_att_db10, 3 << 12, Fastest, Db10);
en_smute_test!(en_smute_rate_slowest_att_db10, 0xF << 12, Slowest, Db10);

write_powercfg_test!(set_stereo, 0, set_output_mode, OutputMode::Stereo);
write_powercfg_test!(set_mono, BF::MONO, set_output_mode, OutputMode::Mono);

#[macro_export]
macro_rules! write_syscfg1_test {
    ($name:ident, $value:expr, $method:ident $(, $arg:expr)*) => {
        write_test!($name, $value, 16, 3, $method $(, $arg)*);
    };
}

write_syscfg1_test!(set_de_75, 0, set_deemphasis, DeEmphasis::Us75);
write_syscfg1_test!(set_de_50, BF::DE, set_deemphasis, DeEmphasis::Us50);

write_syscfg1_test!(enable_agc, 0, enable_auto_gain_control);
write_syscfg1_test!(disable_agc, BF::AGCD, disable_auto_gain_control);

write_syscfg1_test!(
    set_blend_default,
    0,
    set_stereo_to_mono_blend_level,
    Blend::Dbuv31_49
);

write_syscfg1_test!(
    set_blend_minus12db,
    2 << 6,
    set_stereo_to_mono_blend_level,
    Blend::Dbuv19_37
);

write_syscfg1_test!(
    set_blend_minus6db,
    3 << 6,
    set_stereo_to_mono_blend_level,
    Blend::Dbuv25_43
);

write_syscfg1_test!(
    set_blend_plus6b,
    1 << 6,
    set_stereo_to_mono_blend_level,
    Blend::Dbuv37_55
);

#[macro_export]
macro_rules! set_vol_test {
    ($name:ident, $sysconfig2:expr, $sysconfig3:expr, $volume:ident) => {
        write_test!(
            $name,
            16,
            3,
            $sysconfig2,
            4,
            $sysconfig3,
            set_volume,
            Volume::$volume
        );
    };
}

set_vol_test!(set_vol_m58, 1, BF::VOLEXT, Dbfsm58);
set_vol_test!(set_vol_m56, 2, BF::VOLEXT, Dbfsm56);
set_vol_test!(set_vol_m54, 3, BF::VOLEXT, Dbfsm54);
set_vol_test!(set_vol_m52, 4, BF::VOLEXT, Dbfsm52);
set_vol_test!(set_vol_m50, 5, BF::VOLEXT, Dbfsm50);
set_vol_test!(set_vol_m48, 6, BF::VOLEXT, Dbfsm48);
set_vol_test!(set_vol_m46, 7, BF::VOLEXT, Dbfsm46);
set_vol_test!(set_vol_m44, 8, BF::VOLEXT, Dbfsm44);
set_vol_test!(set_vol_m42, 9, BF::VOLEXT, Dbfsm42);
set_vol_test!(set_vol_m40, 10, BF::VOLEXT, Dbfsm40);
set_vol_test!(set_vol_m38, 11, BF::VOLEXT, Dbfsm38);
set_vol_test!(set_vol_m36, 12, BF::VOLEXT, Dbfsm36);
set_vol_test!(set_vol_m34, 13, BF::VOLEXT, Dbfsm34);
set_vol_test!(set_vol_m32, 14, BF::VOLEXT, Dbfsm32);
set_vol_test!(set_vol_m30, 15, BF::VOLEXT, Dbfsm30);
set_vol_test!(set_vol_m28, 1, 0, Dbfsm28);
set_vol_test!(set_vol_m26, 2, 0, Dbfsm26);
set_vol_test!(set_vol_m24, 3, 0, Dbfsm24);
set_vol_test!(set_vol_m22, 4, 0, Dbfsm22);
set_vol_test!(set_vol_m20, 5, 0, Dbfsm20);
set_vol_test!(set_vol_m18, 6, 0, Dbfsm18);
set_vol_test!(set_vol_m16, 7, 0, Dbfsm16);
set_vol_test!(set_vol_m14, 8, 0, Dbfsm14);
set_vol_test!(set_vol_m12, 9, 0, Dbfsm12);
set_vol_test!(set_vol_m10, 10, 0, Dbfsm10);
set_vol_test!(set_vol_m8, 11, 0, Dbfsm8);
set_vol_test!(set_vol_m6, 12, 0, Dbfsm6);
set_vol_test!(set_vol_m4, 13, 0, Dbfsm4);
set_vol_test!(set_vol_m2, 14, 0, Dbfsm2);
set_vol_test!(set_vol_0, 15, 0, Dbfs0);
write_test!(can_set_mute_vol, 0, 16, 4, set_volume, Volume::Mute);

write_test!(band_87_5_108, 0, 16, 4, set_band, Band::Mhz875_108);
write_test!(band_76_108, 1 << 6, 16, 4, set_band, Band::Mhz76_108);
write_test!(band_76_90, 2 << 6, 16, 4, set_band, Band::Mhz76_90);

write_test!(spc_200, 0, 16, 4, set_channel_spacing, Spacing::Khz200);
write_test!(spc_100, 1 << 4, 16, 4, set_channel_spacing, Spacing::Khz100);
write_test!(spc_50, 2 << 4, 16, 4, set_channel_spacing, Spacing::Khz50);

write_test!(gpio1_hi, 0, 16, 3, set_gpio1, Gpio1Config::HighImpedance);
write_test!(gpio1_high, 3, 16, 3, set_gpio1, Gpio1Config::High);
write_test!(gpio1_low, 2, 16, 3, set_gpio1, Gpio1Config::Low);

const STC_RDS_INT: Gpio2Config = Gpio2Config::StcRdsInterrupt;
write_test!(gpio2_hi, 0, 16, 3, set_gpio2, Gpio2Config::HighImpedance);
write_test!(gpio2_int, 1 << 2, 16, 3, set_gpio2, STC_RDS_INT);
write_test!(gpio2_high, 3 << 2, 16, 3, set_gpio2, Gpio2Config::High);
write_test!(gpio2_low, 2 << 2, 16, 3, set_gpio2, Gpio2Config::Low);

const STEREO_IND: Gpio3Config = Gpio3Config::MonoStereoIndicator;
write_test!(gpio3_hi, 0, 16, 3, set_gpio3, Gpio3Config::HighImpedance);
write_test!(gpio3_stereo, 1 << 4, 16, 3, set_gpio3, STEREO_IND);
write_test!(gpio3_high, 3 << 4, 16, 3, set_gpio3, Gpio3Config::High);
write_test!(gpio3_low, 2 << 4, 16, 3, set_gpio3, Gpio3Config::Low);

write_test!(dis_stci, 0, 16, 3, disable_stc_interrupts);
write_test!(en_stci, BF::STCIEN, 16, 3, enable_stc_interrupts);

macro_rules! get_channel_test {
    ($name:ident, $sysconfig2:expr, $readchan:expr, $value:expr) => {
        #[test]
        fn $name() {
            let mut data = [0; 32];
            data[2] = ($readchan >> 8) as u8;
            data[3] = $readchan as u8;
            data[11 * 2] = ($sysconfig2 >> 8) as u8;
            data[11 * 2 + 1] = $sysconfig2 as u8;
            let transactions = [I2cTrans::read(DEV_ADDR, data.to_vec())];
            let mut dev = new_si4703(&transactions);
            let channel = dev.channel().unwrap();
            assert!(($value - 0.2) < channel);
            assert!(($value + 0.2) > channel);
            destroy(dev);
        }
    };
}

get_channel_test!(get_channel_87_base, 0, 0, 87.5);
get_channel_test!(get_channel_87_base_readchan, 0, 100_u16, 87.5 + 100.0 * 0.2);
get_channel_test!(get_channel_76_base, 1 << 6, 100_u16, 76.0 + 100.0 * 0.2);
get_channel_test!(get_channel_0_1_sp, 1 << 4, 100_u16, 87.5 + 100.0 * 0.1);
get_channel_test!(get_channel_0_05_sp, 2 << 4, 100_u16, 87.5 + 100.0 * 0.05);
get_channel_test!(get_chan_comb, 1 << 6 | 2 << 4, 100_u16, 76.0 + 100.0 * 0.05);

read_test!(out_mode_mono, 0, 2, OutputMode::Mono, output_mode);
read_test!(out_mode_stereo, BF::ST, 2, OutputMode::Stereo, output_mode);

#[test]
fn can_read_device_id() {
    let mut data = [0; 32];
    data[6 * 2] = 0x12;
    data[6 * 2 + 1] = 0x42;
    let transactions = [I2cTrans::read(DEV_ADDR, data.to_vec())];
    let mut dev = new_si4703(&transactions);
    let (pn, mfid) = dev.device_id().unwrap();
    assert_eq!(pn, 1);
    assert_eq!(mfid, 0x242);
    destroy(dev);
}

#[test]
fn can_read_chip_id() {
    let mut data = [0; 32];
    data[7 * 2] = 0x85;
    data[7 * 2 + 1] = 0xA5;
    let transactions = [I2cTrans::read(DEV_ADDR, data.to_vec())];
    let mut dev = new_si4703(&transactions);
    let (rev, device, firmware) = dev.chip_id().unwrap();
    assert_eq!(rev, 33);
    assert_eq!(device, 6);
    assert_eq!(firmware, 37);
    destroy(dev);
}