#![no_std]
mod registers;
use embedded_hal::blocking::i2c::{Write, WriteRead};
use registers::{
AutoCalibrationCompensationBackEmfReg, AutoCalibrationCompensationReg, BrakeTimeOffsetReg,
Control1Reg, Control2Reg, Control3Reg, Control4Reg, Control5Reg, FeedbackControlReg, GoReg,
LibrarySelectionReg, ModeReg, OverdriveClampReg, OverdriveTimeOffsetReg, RatedVoltageReg,
RealTimePlaybackInputReg, Register, StatusReg, SustainTimeOffsetNegativeReg,
SustainTimeOffsetPositiveReg, Waveform0Reg,
};
pub use registers::{Effect, Library};
pub struct Drv2605l<I2C, E>
where
I2C: WriteRead<Error = E> + Write<Error = E>,
{
i2c: I2C,
lra: bool,
}
#[allow(unused)]
impl<I2C, E> Drv2605l<I2C, E>
where
I2C: WriteRead<Error = E> + Write<Error = E>,
{
pub fn new(i2c: I2C, calibration: Calibration, lra: bool) -> Result<Self, DrvError> {
let mut haptic = Self { i2c, lra };
haptic.check_id(7)?;
match calibration {
Calibration::Otp => {
if !haptic.is_otp()? {
return Err(DrvError::OTPNotProgrammed);
}
}
Calibration::Load(c) => haptic.set_calibration(c)?,
Calibration::Auto(c) => {
let mut feedback: FeedbackControlReg = Default::default();
let mut ctrl2: Control2Reg = Default::default();
let mut ctrl4: Control4Reg = Default::default();
let mut ctrl1: Control1Reg = Default::default();
let mut rated = RatedVoltageReg(c.rated_voltage);
let mut clamp = OverdriveClampReg(c.overdrive_voltage_clamp);
feedback.set_fb_brake_factor(c.brake_factor);
feedback.set_loop_gain(c.loop_gain);
if (lra) {
feedback.set_n_erm_lra(true);
}
ctrl2.set_sample_time(c.lra_sample_time);
ctrl2.set_blanking_time(c.lra_blanking_time);
ctrl2.set_idiss_time(c.lra_idiss_time);
ctrl4.set_auto_cal_time(c.auto_cal_time);
ctrl4.set_zc_det_time(c.lra_zc_det_time);
ctrl1.set_drive_time(c.drive_time);
haptic.write(feedback)?;
haptic.write(ctrl2)?;
haptic.write(ctrl4)?;
haptic.write(rated)?;
haptic.write(clamp)?;
haptic.write(ctrl1)?;
haptic.calibrate()?;
}
}
haptic.set_standby(true)?;
Ok(haptic)
}
pub fn set_mode(&mut self, mode: Mode) -> Result<(), DrvError> {
let mut m: ModeReg = self.read()?;
let mut ctrl3: Control3Reg = self.read()?;
match mode {
Mode::Pwm => {
if !self.lra {
ctrl3.set_erm_open_loop(false);
}
ctrl3.set_n_pwm_analog(false);
self.write(ctrl3)?;
m.set_mode(registers::Mode::PwmInputAndAnalogInput as u8);
self.write(m)
}
Mode::Rom(library, options) => {
let mut ctrl5: Control5Reg = self.read()?;
ctrl5.set_playback_interval(options.decrease_playback_interval);
self.write(ctrl5)?;
let mut overdrive = OverdriveTimeOffsetReg(options.overdrive_time_offset);
self.write(overdrive)?;
let mut sustain_p = SustainTimeOffsetPositiveReg(options.sustain_positive_offset);
self.write(sustain_p)?;
let mut sustain_n = SustainTimeOffsetNegativeReg(options.sustain_negative_offset);
self.write(sustain_n)?;
let mut brake = BrakeTimeOffsetReg(options.brake_time_offset);
self.write(brake)?;
if !self.lra {
ctrl3.set_erm_open_loop(true);
}
self.write(ctrl3)?;
let mut lib: LibrarySelectionReg = self.read()?;
lib.set_library_selection(library as u8);
self.write(lib)?;
m.set_mode(registers::Mode::InternalTrigger as u8);
self.write(m)
}
Mode::Analog => {
if !self.lra {
ctrl3.set_erm_open_loop(false);
}
ctrl3.set_n_pwm_analog(true);
self.write(ctrl3)?;
m.set_mode(registers::Mode::PwmInputAndAnalogInput as u8);
self.write(m)
}
Mode::RealTimePlayback => {
ctrl3.set_data_format_rtp(true);
if !self.lra {
ctrl3.set_erm_open_loop(false);
}
self.write(ctrl3)?;
m.set_mode(registers::Mode::RealTimePlayback as u8);
self.write(m)
}
}
}
pub fn set_rom(&mut self, roms: &[Effect; 8]) -> Result<(), DrvError> {
let buf: [u8; 9] = [
Waveform0Reg::ADDRESS,
roms[0].into(),
roms[1].into(),
roms[2].into(),
roms[3].into(),
roms[4].into(),
roms[5].into(),
roms[6].into(),
roms[7].into(),
];
self.i2c
.write(ADDRESS, &buf)
.map_err(|_| DrvError::ConnectionError)
}
pub fn set_rom_single(&mut self, rom: Effect) -> Result<(), DrvError> {
let buf: [u8; 3] = [Waveform0Reg::ADDRESS, rom.into(), Effect::Stop.into()];
self.i2c
.write(ADDRESS, &buf)
.map_err(|_| DrvError::ConnectionError)
}
pub fn set_rtp(&mut self, duty: u8) -> Result<(), DrvError> {
let rtp = RealTimePlaybackInputReg(duty);
self.write(rtp)
}
pub fn rtp(&mut self) -> Result<u8, DrvError> {
let rtp: RealTimePlaybackInputReg = self.read()?;
Ok(rtp.value())
}
pub fn set_go(&mut self) -> Result<(), DrvError> {
let mut go: GoReg = self.read()?;
go.set_go(true);
self.write(go)
}
pub fn go(&mut self) -> Result<bool, DrvError> {
Ok(self.read::<GoReg>()?.go())
}
pub fn set_standby(&mut self, enable: bool) -> Result<(), DrvError> {
let mut mode: ModeReg = self.read()?;
mode.set_standby(enable);
self.write(mode)
}
pub fn status(&mut self) -> Result<u8, DrvError> {
let status: StatusReg = self.read()?;
Ok(status.value())
}
pub fn calibration(&mut self) -> Result<LoadParams, DrvError> {
let feedback: FeedbackControlReg = self.read()?;
let compenstation: AutoCalibrationCompensationReg = self.read()?;
let back_emf: AutoCalibrationCompensationBackEmfReg = self.read()?;
Ok(LoadParams {
back_emf_gain: feedback.bemf_gain(),
compenstation: compenstation.value(),
back_emf: back_emf.value(),
})
}
fn write<REG>(&mut self, register: REG) -> Result<(), DrvError>
where
REG: Register,
{
self.i2c
.write(ADDRESS, &[REG::ADDRESS, register.value()])
.map_err(|_| DrvError::ConnectionError)
}
fn read<REG>(&mut self) -> Result<REG, DrvError>
where
REG: Register + From<u8>,
{
let mut buf = [0u8; 1];
self.i2c
.write_read(ADDRESS, &[REG::ADDRESS], &mut buf)
.map_err(|_| DrvError::ConnectionError)?;
Ok(buf[0].into())
}
fn check_id(&mut self, id: u8) -> Result<(), DrvError> {
let reg = StatusReg(self.status()?);
if reg.device_id() != id {
return Err(DrvError::WrongDeviceId);
}
Ok(())
}
fn reset(&mut self) -> Result<(), DrvError> {
let mut mode = ModeReg::default();
mode.set_dev_reset(true);
self.write(mode)?;
while self.read::<ModeReg>()?.dev_reset() {}
Ok(())
}
fn set_calibration(&mut self, load: LoadParams) -> Result<(), DrvError> {
let mut fbcr: FeedbackControlReg = self.read()?;
fbcr.set_bemf_gain(load.back_emf_gain);
self.write(fbcr)?;
let auto_cal_comp = AutoCalibrationCompensationReg(load.compenstation);
self.write(auto_cal_comp)?;
let back_emf = AutoCalibrationCompensationBackEmfReg(load.back_emf);
self.write(back_emf)
}
fn diagnostics(&mut self) -> Result<(), DrvError> {
let mut mode: ModeReg = self.read()?;
mode.set_standby(false);
mode.set_mode(registers::Mode::Diagnostics as u8);
self.write(mode)?;
self.set_go()?;
while self.read::<GoReg>()?.go() {}
let reg = StatusReg(self.status()?);
if reg.diagnostic_result() {
return Err(DrvError::DeviceDiagnosticFailed);
}
Ok(())
}
fn calibrate(&mut self) -> Result<LoadParams, DrvError> {
let mut mode: ModeReg = self.read()?;
mode.set_standby(false);
mode.set_mode(registers::Mode::AutoCalibration as u8);
self.write(mode)?;
self.set_go()?;
while self.read::<GoReg>()?.go() {}
let reg = StatusReg(self.status()?);
if reg.diagnostic_result() {
return Err(DrvError::CalibrationFailed);
}
self.calibration()
}
fn is_otp(&mut self) -> Result<bool, DrvError> {
let reg4: Control4Reg = self.read()?;
Ok(reg4.otp_status())
}
}
#[allow(unused)]
#[derive(Debug)]
pub enum DrvError {
WrongMotorType,
WrongDeviceId,
ConnectionError,
DeviceDiagnosticFailed,
CalibrationFailed,
OTPNotProgrammed,
}
const ADDRESS: u8 = 0x5a;
pub enum Calibration {
Auto(CalibrationParams),
Load(LoadParams),
Otp,
}
pub struct LoadParams {
pub compenstation: u8,
pub back_emf: u8,
pub back_emf_gain: u8,
}
#[non_exhaustive]
pub struct CalibrationParams {
pub rated_voltage: u8,
pub overdrive_voltage_clamp: u8,
pub drive_time: u8,
pub brake_factor: u8,
pub loop_gain: u8,
pub auto_cal_time: u8,
pub lra_sample_time: u8,
pub lra_blanking_time: u8,
pub lra_idiss_time: u8,
pub lra_zc_det_time: u8,
}
impl Default for CalibrationParams {
fn default() -> Self {
Self {
brake_factor: 2,
loop_gain: 2,
lra_sample_time: 3,
lra_blanking_time: 1,
lra_idiss_time: 1,
auto_cal_time: 3,
lra_zc_det_time: 0,
rated_voltage: 0x3E,
overdrive_voltage_clamp: 0x8C,
drive_time: 0x13,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct RomParams {
pub overdrive_time_offset: u8,
pub sustain_positive_offset: u8,
pub sustain_negative_offset: u8,
pub brake_time_offset: u8,
pub decrease_playback_interval: bool,
}
impl Default for RomParams {
fn default() -> Self {
Self {
overdrive_time_offset: 0,
sustain_positive_offset: 0,
sustain_negative_offset: 0,
brake_time_offset: 0,
decrease_playback_interval: false,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum Mode {
Rom(Library, RomParams),
Pwm,
Analog,
RealTimePlayback,
}