use crate::datagram::{ReadRequest, ReadResponse, ResponseReader, WriteRequest};
use crate::error::Error;
use crate::registers::{
Chopconf, Coolconf, DrvStatus, Gconf, Gstat, Ifcnt, IholdIrun, Ioin, MicrostepResolution,
Mscnt, Pwmconf, ReadableRegister, SgResult, Sgthrs, Tcoolthrs, Tpwmthrs, Tstep, Vactual,
WritableRegister,
};
pub struct Tmc2209<U> {
uart: U,
slave_addr: u8,
reader: ResponseReader,
}
impl<U> Tmc2209<U> {
pub fn new(uart: U, slave_addr: u8) -> Self {
assert!(slave_addr <= 3, "Slave address must be 0-3");
Self {
uart,
slave_addr,
reader: ResponseReader::new(),
}
}
pub fn slave_addr(&self) -> u8 {
self.slave_addr
}
pub fn set_slave_addr(&mut self, addr: u8) {
assert!(addr <= 3, "Slave address must be 0-3");
self.slave_addr = addr;
}
pub fn uart(&self) -> &U {
&self.uart
}
pub fn uart_mut(&mut self) -> &mut U {
&mut self.uart
}
pub fn release(self) -> U {
self.uart
}
fn read_request<R: ReadableRegister>(&self) -> ReadRequest {
ReadRequest::new(self.slave_addr, R::ADDRESS)
}
fn write_request<R: WritableRegister>(&self, reg: &R) -> WriteRequest {
WriteRequest::new(self.slave_addr, R::ADDRESS, (*reg).into())
}
}
#[cfg(feature = "blocking")]
impl<U, E> Tmc2209<U>
where
U: embedded_io::Read<Error = E> + embedded_io::Write<Error = E>,
{
pub fn read_register<R: ReadableRegister>(&mut self) -> Result<R, Error<E>> {
let request = self.read_request::<R>();
self.uart
.write_all(request.as_bytes())
.map_err(Error::Uart)?;
self.uart.flush().map_err(Error::Uart)?;
let mut echo_buf = [0u8; 4];
self.read_exact(&mut echo_buf)?;
let response = self.read_response()?;
let expected_addr = R::ADDRESS as u8;
if response.reg_addr() != expected_addr {
return Err(Error::AddressMismatch {
expected: expected_addr,
actual: response.reg_addr(),
});
}
Ok(R::from(response.data()))
}
pub fn write_register<R: WritableRegister>(&mut self, reg: &R) -> Result<(), Error<E>> {
let request = self.write_request(reg);
self.uart
.write_all(request.as_bytes())
.map_err(Error::Uart)?;
self.uart.flush().map_err(Error::Uart)?;
let mut echo_buf = [0u8; 8];
self.read_exact(&mut echo_buf)?;
Ok(())
}
pub fn read_raw(&mut self, reg_addr: u8) -> Result<u32, Error<E>> {
let request = ReadRequest::from_raw_addr(self.slave_addr, reg_addr);
self.uart
.write_all(request.as_bytes())
.map_err(Error::Uart)?;
self.uart.flush().map_err(Error::Uart)?;
let mut echo_buf = [0u8; 4];
self.read_exact(&mut echo_buf)?;
let response = self.read_response()?;
Ok(response.data())
}
pub fn write_raw(&mut self, reg_addr: u8, data: u32) -> Result<(), Error<E>> {
let request = WriteRequest::from_raw(self.slave_addr, reg_addr, data);
self.uart
.write_all(request.as_bytes())
.map_err(Error::Uart)?;
self.uart.flush().map_err(Error::Uart)?;
let mut echo_buf = [0u8; 8];
self.read_exact(&mut echo_buf)?;
Ok(())
}
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error<E>> {
let mut total_read = 0;
while total_read < buf.len() {
let n = self.uart.read(&mut buf[total_read..]).map_err(Error::Uart)?;
if n == 0 {
return Err(Error::NoResponse);
}
total_read += n;
}
Ok(())
}
fn read_response(&mut self) -> Result<ReadResponse, Error<E>> {
self.reader.reset();
let mut buf = [0u8; 8];
self.read_exact(&mut buf)?;
let (_, result) = self.reader.feed(&buf);
result.ok_or(Error::NoResponse)?
}
pub fn is_connected(&mut self) -> bool {
self.read_register::<Ifcnt>().is_ok()
}
pub fn ifcnt(&mut self) -> Result<u8, Error<E>> {
let reg = self.read_register::<Ifcnt>()?;
Ok(reg.count())
}
pub fn gstat(&mut self) -> Result<Gstat, Error<E>> {
self.read_register()
}
pub fn clear_gstat(&mut self) -> Result<(), Error<E>> {
let gstat = Gstat::from(0x07);
self.write_register(&gstat)
}
pub fn ioin(&mut self) -> Result<Ioin, Error<E>> {
self.read_register()
}
pub fn drv_status(&mut self) -> Result<DrvStatus, Error<E>> {
self.read_register()
}
pub fn tstep(&mut self) -> Result<u32, Error<E>> {
let reg = self.read_register::<Tstep>()?;
Ok(reg.tstep())
}
pub fn sg_result(&mut self) -> Result<u16, Error<E>> {
let reg = self.read_register::<SgResult>()?;
Ok(reg.result())
}
pub fn mscnt(&mut self) -> Result<u16, Error<E>> {
let reg = self.read_register::<Mscnt>()?;
Ok(reg.count())
}
pub fn set_current(
&mut self,
run_current: u8,
hold_current: u8,
hold_delay: u8,
) -> Result<(), Error<E>> {
let mut reg = IholdIrun::new();
reg.set_irun(run_current)
.set_ihold(hold_current)
.set_iholddelay(hold_delay);
self.write_register(®)
}
pub fn set_microsteps(&mut self, resolution: MicrostepResolution) -> Result<(), Error<E>> {
let mut chopconf = self.read_register::<Chopconf>()?;
chopconf.set_microstep_resolution(resolution);
self.write_register(&chopconf)
}
pub fn set_enabled(&mut self, enabled: bool) -> Result<(), Error<E>> {
let mut chopconf = self.read_register::<Chopconf>()?;
if enabled {
if chopconf.toff() == 0 {
chopconf.set_toff(3);
}
} else {
chopconf.set_toff(0);
}
self.write_register(&chopconf)
}
pub fn set_velocity(&mut self, velocity: i32) -> Result<(), Error<E>> {
let mut reg = Vactual::new();
reg.set_velocity(velocity);
self.write_register(®)
}
pub fn stop(&mut self) -> Result<(), Error<E>> {
self.set_velocity(0)
}
pub fn set_stall_threshold(&mut self, threshold: u8) -> Result<(), Error<E>> {
let mut reg = Sgthrs::new();
reg.set_threshold(threshold);
self.write_register(®)
}
pub fn enable_stealthchop(&mut self) -> Result<(), Error<E>> {
let mut gconf = self.read_register::<Gconf>()?;
gconf.set_en_spreadcycle(false);
self.write_register(&gconf)
}
pub fn enable_spreadcycle(&mut self) -> Result<(), Error<E>> {
let mut gconf = self.read_register::<Gconf>()?;
gconf.set_en_spreadcycle(true);
self.write_register(&gconf)
}
pub fn is_standstill(&mut self) -> Result<bool, Error<E>> {
let status = self.drv_status()?;
Ok(status.stst())
}
pub fn is_overtemperature_warning(&mut self) -> Result<bool, Error<E>> {
let status = self.drv_status()?;
Ok(status.otpw())
}
pub fn is_overtemperature_shutdown(&mut self) -> Result<bool, Error<E>> {
let status = self.drv_status()?;
Ok(status.ot())
}
pub fn enable_coolstep(&mut self, semin: u8, semax: u8) -> Result<(), Error<E>> {
let mut coolconf = Coolconf::new();
coolconf
.set_semin(semin.min(15))
.set_semax(semax.min(15))
.set_seup(0) .set_sedn(0); self.write_register(&coolconf)
}
pub fn disable_coolstep(&mut self) -> Result<(), Error<E>> {
let coolconf = Coolconf::new(); self.write_register(&coolconf)
}
pub fn set_coolstep_threshold(&mut self, threshold: u32) -> Result<(), Error<E>> {
let mut tcoolthrs = Tcoolthrs::new();
tcoolthrs.set_threshold(threshold);
self.write_register(&tcoolthrs)
}
pub fn set_stealthchop_threshold(&mut self, threshold: u32) -> Result<(), Error<E>> {
let mut tpwmthrs = Tpwmthrs::new();
tpwmthrs.set_threshold(threshold);
self.write_register(&tpwmthrs)
}
pub fn configure_stall_detection(&mut self, threshold: u8) -> Result<(), Error<E>> {
self.set_stall_threshold(threshold)?;
let mut gconf = self.read_register::<Gconf>()?;
gconf.set_diag0_stall(true);
self.write_register(&gconf)?;
Ok(())
}
pub fn is_stalled(&mut self) -> Result<bool, Error<E>> {
let sg_result = self.sg_result()?;
Ok(sg_result == 0)
}
pub fn load_indicator(&mut self) -> Result<u16, Error<E>> {
self.sg_result()
}
pub fn configure_stealthchop(
&mut self,
pwm_ofs: u8,
pwm_grad: u8,
autoscale: bool,
autograd: bool,
) -> Result<(), Error<E>> {
let mut pwmconf = self.read_register::<Pwmconf>()?;
pwmconf
.set_pwm_ofs(pwm_ofs)
.set_pwm_grad(pwm_grad)
.set_pwm_autoscale(autoscale)
.set_pwm_autograd(autograd);
self.write_register(&pwmconf)
}
pub fn set_vsense(&mut self, high_sensitivity: bool) -> Result<(), Error<E>> {
let mut chopconf = self.read_register::<Chopconf>()?;
chopconf.set_vsense(high_sensitivity);
self.write_register(&chopconf)
}
pub fn configure_chopper(
&mut self,
toff: u8,
hstrt: u8,
hend: u8,
tbl: u8,
) -> Result<(), Error<E>> {
let mut chopconf = self.read_register::<Chopconf>()?;
chopconf
.set_toff(toff.min(15))
.set_hstrt(hstrt.min(7))
.set_hend(hend.min(15))
.set_tbl(tbl.min(3));
self.write_register(&chopconf)
}
pub fn set_interpolation(&mut self, enabled: bool) -> Result<(), Error<E>> {
let mut chopconf = self.read_register::<Chopconf>()?;
chopconf.set_intpol(enabled);
self.write_register(&chopconf)
}
pub fn actual_current_scale(&mut self) -> Result<u8, Error<E>> {
let status = self.drv_status()?;
Ok(status.cs_actual())
}
pub fn is_stealthchop_active(&mut self) -> Result<bool, Error<E>> {
let status = self.drv_status()?;
Ok(status.stealth())
}
pub fn status_summary(&mut self) -> Result<(bool, bool, bool), Error<E>> {
let status = self.drv_status()?;
let errors = status.has_error();
let warnings = status.otpw() || status.open_load_detected();
let running = !status.stst();
Ok((errors, warnings, running))
}
}
#[cfg(feature = "async")]
impl<U, E> Tmc2209<U>
where
U: embedded_io_async::Read<Error = E> + embedded_io_async::Write<Error = E>,
{
pub async fn read_register_async<R: ReadableRegister>(&mut self) -> Result<R, Error<E>> {
let request = self.read_request::<R>();
self.uart
.write_all(request.as_bytes())
.await
.map_err(Error::Uart)?;
self.uart.flush().await.map_err(Error::Uart)?;
let mut echo_buf = [0u8; 4];
self.read_exact_async(&mut echo_buf).await?;
let response = self.read_response_async().await?;
let expected_addr = R::ADDRESS as u8;
if response.reg_addr() != expected_addr {
return Err(Error::AddressMismatch {
expected: expected_addr,
actual: response.reg_addr(),
});
}
Ok(R::from(response.data()))
}
pub async fn write_register_async<R: WritableRegister>(
&mut self,
reg: &R,
) -> Result<(), Error<E>> {
let request = self.write_request(reg);
self.uart
.write_all(request.as_bytes())
.await
.map_err(Error::Uart)?;
self.uart.flush().await.map_err(Error::Uart)?;
let mut echo_buf = [0u8; 8];
self.read_exact_async(&mut echo_buf).await?;
Ok(())
}
pub async fn read_raw_async(&mut self, reg_addr: u8) -> Result<u32, Error<E>> {
let request = ReadRequest::from_raw_addr(self.slave_addr, reg_addr);
self.uart
.write_all(request.as_bytes())
.await
.map_err(Error::Uart)?;
self.uart.flush().await.map_err(Error::Uart)?;
let mut echo_buf = [0u8; 4];
self.read_exact_async(&mut echo_buf).await?;
let response = self.read_response_async().await?;
Ok(response.data())
}
pub async fn write_raw_async(&mut self, reg_addr: u8, data: u32) -> Result<(), Error<E>> {
let request = WriteRequest::from_raw(self.slave_addr, reg_addr, data);
self.uart
.write_all(request.as_bytes())
.await
.map_err(Error::Uart)?;
self.uart.flush().await.map_err(Error::Uart)?;
let mut echo_buf = [0u8; 8];
self.read_exact_async(&mut echo_buf).await?;
Ok(())
}
async fn read_exact_async(&mut self, buf: &mut [u8]) -> Result<(), Error<E>> {
let mut total_read = 0;
while total_read < buf.len() {
let n = self
.uart
.read(&mut buf[total_read..])
.await
.map_err(Error::Uart)?;
if n == 0 {
return Err(Error::NoResponse);
}
total_read += n;
}
Ok(())
}
async fn read_response_async(&mut self) -> Result<ReadResponse, Error<E>> {
self.reader.reset();
let mut buf = [0u8; 8];
self.read_exact_async(&mut buf).await?;
let (_, result) = self.reader.feed(&buf);
result.ok_or(Error::NoResponse)?
}
pub async fn is_connected_async(&mut self) -> bool {
self.read_register_async::<Ifcnt>().await.is_ok()
}
pub async fn ifcnt_async(&mut self) -> Result<u8, Error<E>> {
let reg = self.read_register_async::<Ifcnt>().await?;
Ok(reg.count())
}
pub async fn drv_status_async(&mut self) -> Result<DrvStatus, Error<E>> {
self.read_register_async().await
}
pub async fn set_current_async(
&mut self,
run_current: u8,
hold_current: u8,
hold_delay: u8,
) -> Result<(), Error<E>> {
let mut reg = IholdIrun::new();
reg.set_irun(run_current)
.set_ihold(hold_current)
.set_iholddelay(hold_delay);
self.write_register_async(®).await
}
pub async fn set_microsteps_async(
&mut self,
resolution: MicrostepResolution,
) -> Result<(), Error<E>> {
let mut chopconf = self.read_register_async::<Chopconf>().await?;
chopconf.set_microstep_resolution(resolution);
self.write_register_async(&chopconf).await
}
pub async fn set_velocity_async(&mut self, velocity: i32) -> Result<(), Error<E>> {
let mut reg = Vactual::new();
reg.set_velocity(velocity);
self.write_register_async(®).await
}
pub async fn stop_async(&mut self) -> Result<(), Error<E>> {
self.set_velocity_async(0).await
}
pub async fn enable_coolstep_async(&mut self, semin: u8, semax: u8) -> Result<(), Error<E>> {
let mut coolconf = Coolconf::new();
coolconf
.set_semin(semin.min(15))
.set_semax(semax.min(15))
.set_seup(0)
.set_sedn(0);
self.write_register_async(&coolconf).await
}
pub async fn disable_coolstep_async(&mut self) -> Result<(), Error<E>> {
let coolconf = Coolconf::new();
self.write_register_async(&coolconf).await
}
pub async fn set_coolstep_threshold_async(&mut self, threshold: u32) -> Result<(), Error<E>> {
let mut tcoolthrs = Tcoolthrs::new();
tcoolthrs.set_threshold(threshold);
self.write_register_async(&tcoolthrs).await
}
pub async fn set_stealthchop_threshold_async(
&mut self,
threshold: u32,
) -> Result<(), Error<E>> {
let mut tpwmthrs = Tpwmthrs::new();
tpwmthrs.set_threshold(threshold);
self.write_register_async(&tpwmthrs).await
}
pub async fn configure_stall_detection_async(&mut self, threshold: u8) -> Result<(), Error<E>> {
let mut sgthrs = Sgthrs::new();
sgthrs.set_threshold(threshold);
self.write_register_async(&sgthrs).await?;
let mut gconf = self.read_register_async::<Gconf>().await?;
gconf.set_diag0_stall(true);
self.write_register_async(&gconf).await?;
Ok(())
}
pub async fn is_stalled_async(&mut self) -> Result<bool, Error<E>> {
let sg = self.read_register_async::<SgResult>().await?;
Ok(sg.result() == 0)
}
pub async fn load_indicator_async(&mut self) -> Result<u16, Error<E>> {
let sg = self.read_register_async::<SgResult>().await?;
Ok(sg.result())
}
pub async fn enable_stealthchop_async(&mut self) -> Result<(), Error<E>> {
let mut gconf = self.read_register_async::<Gconf>().await?;
gconf.set_en_spreadcycle(false);
self.write_register_async(&gconf).await
}
pub async fn enable_spreadcycle_async(&mut self) -> Result<(), Error<E>> {
let mut gconf = self.read_register_async::<Gconf>().await?;
gconf.set_en_spreadcycle(true);
self.write_register_async(&gconf).await
}
pub async fn set_enabled_async(&mut self, enabled: bool) -> Result<(), Error<E>> {
let mut chopconf = self.read_register_async::<Chopconf>().await?;
if enabled {
if chopconf.toff() == 0 {
chopconf.set_toff(3);
}
} else {
chopconf.set_toff(0);
}
self.write_register_async(&chopconf).await
}
pub async fn is_standstill_async(&mut self) -> Result<bool, Error<E>> {
let status = self.drv_status_async().await?;
Ok(status.stst())
}
}