Library for Trinamic TMC2209 stepper modules to drive bipolar stepper motors. Ported and adapted from https://github.com/teemuatlut/TMCStepper

Dependents:   TMC2209-Test2

TMC2208Stepper.cpp

Committer:
charly
Date:
2021-12-02
Revision:
2:b34e91b54373
Parent:
1:5ba0c258c4ed

File content as of revision 2:b34e91b54373:

#include "TMCStepper.h"
#include "TMC_MACROS.h"
//#include "SERIAL_SWITCH.h"

// Protected
// addr needed for TMC2209
TMC2208Stepper::TMC2208Stepper(Stream * SerialPort, float RS, uint8_t addr) :
    TMCStepper(RS),
    slave_address(addr)
    {
        HWSerial = SerialPort;
        defaults();
    }

TMC2208Stepper::TMC2208Stepper(Stream * SerialPort, float RS, uint8_t addr, uint16_t mul_pin1, uint16_t mul_pin2) :
    TMC2208Stepper(SerialPort, RS)
    {
//        SSwitch *SMulObj = new SSwitch(mul_pin1, mul_pin2, addr);
//        sswitch = SMulObj;
    }

#if SW_CAPABLE_PLATFORM
    // Protected
    // addr needed for TMC2209
    TMC2208Stepper::TMC2208Stepper(PinName SW_RX_pin, PinName SW_TX_pin, float RS, uint8_t addr) :
        TMCStepper(RS),
        RXTX_pin(SW_RX_pin == SW_TX_pin ? SW_RX_pin : 0),
        slave_address(addr)
        {
            BufferedSerial *SWSerialObj = new BufferedSerial(SW_TX_pin, SW_RX_pin);
            SWSerial = SWSerialObj;
            defaults();
        }

    void TMC2208Stepper::beginSerial(uint32_t baudrate) {
        if (SWSerial != nullptr)
        {
            //SWSerial->begin(baudrate);
            //SWSerial->end();
            SWSerial->set_baud(baudrate);
            SWSerial->set_format(
                        /* bits */ 8,
                        /* parity */ BufferedSerial::None,
                        /* stop bit */ 1
                    );
            SWSerial->set_blocking(false);  //set to non-blocking read
        }
//        #if defined(ARDUINO_ARCH_AVR)
//            if (RXTX_pin > 0) {
//                digitalWrite(RXTX_pin, HIGH);
//                pinMode(RXTX_pin, OUTPUT);
//           }
//        #endif
    }
#endif

void TMC2208Stepper::begin() {
    #if SW_CAPABLE_PLATFORM
        beginSerial(115200);
        //beginSerial(9600);
    #endif
    pdn_disable(true);
    mstep_reg_select(true);
    //Wait to initialize
    wait_us(replyDelay*1000);

}

void TMC2208Stepper::defaults() {
    GCONF_register.i_scale_analog = 1;
    GCONF_register.internal_rsense = 0; // OTP
    GCONF_register.en_spreadcycle = 0; // OTP
    GCONF_register.multistep_filt = 1; // OTP
    IHOLD_IRUN_register.iholddelay = 1; // OTP
    TPOWERDOWN_register.sr = 20;
    CHOPCONF_register.sr = 0x10000053;
    PWMCONF_register.sr = 0xC10D0024;
  //MSLUT0_register.sr = ???;
  //MSLUT1_register.sr = ???;
  //MSLUT2_register.sr = ???;
  //MSLUT3_register.sr = ???;
  //MSLUT4_register.sr = ???;
  //MSLUT5_register.sr = ???;
  //MSLUT6_register.sr = ???;
  //MSLUT7_register.sr = ???;
  //MSLUTSTART_register.start_sin90 = 247;
}

void TMC2208Stepper::push() {
    GCONF(GCONF_register.sr);
    IHOLD_IRUN(IHOLD_IRUN_register.sr);
    SLAVECONF(SLAVECONF_register.sr);
    TPOWERDOWN(TPOWERDOWN_register.sr);
    TPWMTHRS(TPWMTHRS_register.sr);
    VACTUAL(VACTUAL_register.sr);
    CHOPCONF(CHOPCONF_register.sr);
    PWMCONF(PWMCONF_register.sr);
}

bool TMC2208Stepper::isEnabled() { return !enn() && toff(); }

uint8_t TMC2208Stepper::calcCRC(uint8_t datagram[], uint8_t len) {
    uint8_t crc = 0;
    for (uint8_t i = 0; i < len; i++) {
        uint8_t currentByte = datagram[i];
        for (uint8_t j = 0; j < 8; j++) {
            if ((crc >> 7) ^ (currentByte & 0x01)) {
                crc = (crc << 1) ^ 0x07;
            } else {
                crc = (crc << 1);
            }
            crc &= 0xff;
            currentByte = currentByte >> 1;
        }
    }
    return crc;
}

__attribute__((weak))
int TMC2208Stepper::available() {
    int out = 0;
    #if SW_CAPABLE_PLATFORM
        if (SWSerial != nullptr) {
//            out = SWSerial->available();
out = 1;
        } else
    #endif
        if (HWSerial != nullptr) {
//            out = HWSerial->available();
out = 1;
        }

    return out;
}

__attribute__((weak))
void TMC2208Stepper::preWriteCommunication() {
    if (HWSerial != nullptr) {
//        if (sswitch != nullptr)
//            sswitch->active();
    }
}

__attribute__((weak))
void TMC2208Stepper::preReadCommunication() {
    #if SW_CAPABLE_PLATFORM
        if (SWSerial != nullptr) {
//            SWSerial->listen();
        } else
    #endif
        if (HWSerial != nullptr) {
//            if (sswitch != nullptr)
//                sswitch->active();
        }
}

__attribute__((weak))
int16_t TMC2208Stepper::serial_read() {
    int16_t out = 0;
    int16_t count = 0;
     
    #if SW_CAPABLE_PLATFORM
        if (SWSerial != nullptr) {
//            out = SWSerial->read();
            count = SWSerial->read(&out, 1); // read one character
//            if (SWSerial->readable()) {
//                SWSerial->read(&out, 1); // read one character
//            }

        } else
    #endif
        if (HWSerial != nullptr) {
//            out = HWSerial->read();
            HWSerial->read(&out, 1); // read one character
        }
    if (count >= 1) {
//       printf("<%02X|",out);
       return out;
    } else {
        return -1;
    }
}

__attribute__((weak))
uint8_t TMC2208Stepper::serial_write(const uint8_t data) {
    int out = 0;
    #if SW_CAPABLE_PLATFORM
        if (SWSerial != nullptr) {
//            printf(">%02X|",data);
            return SWSerial->write(&data,1);
        } else
    #endif
        if (HWSerial != nullptr) {
            return HWSerial->write(&data,1);
        }

    return out;
}

__attribute__((weak))
void TMC2208Stepper::postWriteCommunication() {}

__attribute__((weak))
void TMC2208Stepper::postReadCommunication() {
    #if SW_CAPABLE_PLATFORM
//        if (SWSerial != nullptr) {
//            SWSerial->end();
//        }
    #endif
}

void TMC2208Stepper::write(uint8_t addr, uint32_t regVal) {
    uint8_t len = 7;
    addr |= TMC_WRITE;
    uint8_t datagram[] = {TMC2208_SYNC, slave_address, addr, (uint8_t)(regVal>>24), (uint8_t)(regVal>>16), (uint8_t)(regVal>>8), (uint8_t)(regVal>>0), 0x00};

    datagram[len] = calcCRC(datagram, len);

    preWriteCommunication();

    for(uint8_t i=0; i<=len; i++) {
        bytesWritten += serial_write(datagram[i]);
    }
    postWriteCommunication();

    //delay(replyDelay);
    wait_us(replyDelay*1000);
}

uint64_t TMC2208Stepper::_sendDatagram(uint8_t datagram[], const uint8_t len, uint16_t timeout) {

//  while (available() > 0) serial_read(); // Flush
    SWSerial->sync();
/*    uint8_t dummy;
    while (SWSerial->readable()) {
        SWSerial->read(&dummy, 1); // read one character
    }
*/    
/*    uint8_t dummy;
    while (SWSerial->read(&dummy, 1) >= 0) {
        ;
    }    
*/

    #if defined(ARDUINO_ARCH_AVR)
        if (RXTX_pin > 0) {
            digitalWrite(RXTX_pin, HIGH);
            pinMode(RXTX_pin, OUTPUT);
        }
    #endif

    for(int i=0; i<=len; i++) serial_write(datagram[i]);

    #if defined(ARDUINO_ARCH_AVR)
        if (RXTX_pin > 0) {
            pinMode(RXTX_pin, INPUT_PULLUP);
        }
    #endif

    //delay(this->replyDelay);
    wait_us(this->replyDelay*1000);

    // scan for the rx frame and read it

//    uint32_t ms = millis();
    uint32_t sync_target = (static_cast<uint32_t>(datagram[0])<<16) | 0xFF00 | datagram[2];
    uint32_t sync = 0;
    
    // 64-bit time doesn't wrap for half a billion years, at least
    uint64_t start_ms, now;
    start_ms = Kernel::get_ms_count();


    do {
/*        uint32_t ms2 = millis();
        if (ms2 != ms) {
            // 1ms tick
            ms = ms2;
            timeout--;
        }
        if (!timeout) return 0;
*/
        // 64-bit time doesn't wrap for half a billion years, at least
        now = Kernel::get_ms_count();
        if (now - start_ms > timeout) return 0;
        
        int16_t res = serial_read();
        if (res < 0) continue;

        sync <<= 8;
        sync |= res & 0xFF;
        sync &= 0xFFFFFF;

    } while (sync != sync_target);

    uint64_t out = sync;
 //   ms = millis();
 //   timeout = this->abort_window;
    
    start_ms = Kernel::get_ms_count();
    
    for(uint8_t i=0; i<5;) {
/*        uint32_t ms2 = millis();
        if (ms2 != ms) {
            // 1ms tick
            ms = ms2;
            timeout--;
        }
        if (!timeout) return 0;
*/
        now = Kernel::get_ms_count();
        if (now - start_ms > timeout) return 0;
        
        int16_t res = serial_read();
        if (res < 0) continue;

        out <<= 8;
        out |= res & 0xFF;

        i++;
    }

    #if defined(ARDUINO_ARCH_AVR)
        if (RXTX_pin > 0) {
            digitalWrite(RXTX_pin, HIGH);
            pinMode(RXTX_pin, OUTPUT);
        }
    #endif

//    while (available() > 0) serial_read(); // Flush
    SWSerial->sync();

    return out;


}

uint32_t TMC2208Stepper::read(uint8_t addr) {
    constexpr uint8_t len = 3;
    addr |= TMC_READ;
    uint8_t datagram[] = {TMC2208_SYNC, slave_address, addr, 0x00};
    datagram[len] = calcCRC(datagram, len);
    uint64_t out = 0x00000000UL;

    for (uint8_t i = 0; i < max_retries; i++) {
        preReadCommunication();
        out = _sendDatagram(datagram, len, abort_window);
        postReadCommunication();

//        delay(replyDelay);
//        wait_us(replyDelay*1000);

        CRCerror = false;
        uint8_t out_datagram[] = {
            static_cast<uint8_t>(out>>56),
            static_cast<uint8_t>(out>>48),
            static_cast<uint8_t>(out>>40),
            static_cast<uint8_t>(out>>32),
            static_cast<uint8_t>(out>>24),
            static_cast<uint8_t>(out>>16),
            static_cast<uint8_t>(out>> 8),
            static_cast<uint8_t>(out>> 0)
        };
        uint8_t crc = calcCRC(out_datagram, 7);
        if ((crc != static_cast<uint8_t>(out)) || crc == 0 ) {
            CRCerror = true;
            out = 0;
        } else {
            break;
        }
    }

    return out>>8;
}

uint8_t TMC2208Stepper::IFCNT() {
    return read(IFCNT_t::address);
}

void TMC2208Stepper::SLAVECONF(uint16_t input) {
    SLAVECONF_register.sr = input&0xF00;
    write(SLAVECONF_register.address, SLAVECONF_register.sr);
}
uint16_t TMC2208Stepper::SLAVECONF() {
    return SLAVECONF_register.sr;
}
void TMC2208Stepper::senddelay(uint8_t B)   { SLAVECONF_register.senddelay = B; write(SLAVECONF_register.address, SLAVECONF_register.sr); }
uint8_t TMC2208Stepper::senddelay()         { return SLAVECONF_register.senddelay; }

void TMC2208Stepper::OTP_PROG(uint16_t input) {
    write(OTP_PROG_t::address, input);
}

uint32_t TMC2208Stepper::OTP_READ() {
    return read(OTP_READ_t::address);
}

uint32_t TMC2208Stepper::IOIN() {
    return read(TMC2208_n::IOIN_t::address);
}
bool TMC2208Stepper::enn()          { TMC2208_n::IOIN_t r{0}; r.sr = IOIN(); return r.enn;      }
bool TMC2208Stepper::ms1()          { TMC2208_n::IOIN_t r{0}; r.sr = IOIN(); return r.ms1;      }
bool TMC2208Stepper::ms2()          { TMC2208_n::IOIN_t r{0}; r.sr = IOIN(); return r.ms2;      }
bool TMC2208Stepper::diag()         { TMC2208_n::IOIN_t r{0}; r.sr = IOIN(); return r.diag;     }
bool TMC2208Stepper::pdn_uart()     { TMC2208_n::IOIN_t r{0}; r.sr = IOIN(); return r.pdn_uart; }
bool TMC2208Stepper::step()         { TMC2208_n::IOIN_t r{0}; r.sr = IOIN(); return r.step;     }
bool TMC2208Stepper::sel_a()        { TMC2208_n::IOIN_t r{0}; r.sr = IOIN(); return r.sel_a;    }
bool TMC2208Stepper::dir()          { TMC2208_n::IOIN_t r{0}; r.sr = IOIN(); return r.dir;      }
uint8_t TMC2208Stepper::version()   { TMC2208_n::IOIN_t r{0}; r.sr = IOIN(); return r.version;  }

/*
uint32_t TMC2224Stepper::IOIN() {
    return read(TMC2224_n::IOIN_t::address);
}
bool TMC2224Stepper::enn()          { TMC2224_n::IOIN_t r{0}; r.sr = IOIN(); return r.enn;      }
bool TMC2224Stepper::ms1()          { TMC2224_n::IOIN_t r{0}; r.sr = IOIN(); return r.ms1;      }
bool TMC2224Stepper::ms2()          { TMC2224_n::IOIN_t r{0}; r.sr = IOIN(); return r.ms2;      }
bool TMC2224Stepper::pdn_uart()     { TMC2224_n::IOIN_t r{0}; r.sr = IOIN(); return r.pdn_uart; }
bool TMC2224Stepper::spread()       { TMC2224_n::IOIN_t r{0}; r.sr = IOIN(); return r.spread;   }
bool TMC2224Stepper::step()         { TMC2224_n::IOIN_t r{0}; r.sr = IOIN(); return r.step;     }
bool TMC2224Stepper::sel_a()        { TMC2224_n::IOIN_t r{0}; r.sr = IOIN(); return r.sel_a;    }
bool TMC2224Stepper::dir()          { TMC2224_n::IOIN_t r{0}; r.sr = IOIN(); return r.dir;      }
uint8_t TMC2224Stepper::version()   { TMC2224_n::IOIN_t r{0}; r.sr = IOIN(); return r.version;  }
*/
uint16_t TMC2208Stepper::FACTORY_CONF() {
    return read(FACTORY_CONF_register.address);
}
void TMC2208Stepper::FACTORY_CONF(uint16_t input) {
    FACTORY_CONF_register.sr = input;
    write(FACTORY_CONF_register.address, FACTORY_CONF_register.sr);
}
void TMC2208Stepper::fclktrim(uint8_t B){ FACTORY_CONF_register.fclktrim = B; write(FACTORY_CONF_register.address, FACTORY_CONF_register.sr); }
void TMC2208Stepper::ottrim(uint8_t B)  { FACTORY_CONF_register.ottrim = B; write(FACTORY_CONF_register.address, FACTORY_CONF_register.sr); }
uint8_t TMC2208Stepper::fclktrim()      { FACTORY_CONF_t r{0}; r.sr = FACTORY_CONF(); return r.fclktrim; }
uint8_t TMC2208Stepper::ottrim()        { FACTORY_CONF_t r{0}; r.sr = FACTORY_CONF(); return r.ottrim; }

void TMC2208Stepper::VACTUAL(uint32_t input) {
    VACTUAL_register.sr = input;
    write(VACTUAL_register.address, VACTUAL_register.sr);
}
uint32_t TMC2208Stepper::VACTUAL() {
    return VACTUAL_register.sr;
}

uint32_t TMC2208Stepper::PWM_SCALE() {
    return read(TMC2208_n::PWM_SCALE_t::address);
}
uint8_t TMC2208Stepper::pwm_scale_sum() {
    TMC2208_n::PWM_SCALE_t r{0};
    r.sr = PWM_SCALE();
    return r.pwm_scale_sum;
}

int16_t TMC2208Stepper::pwm_scale_auto() {
    TMC2208_n::PWM_SCALE_t r{0};
    r.sr = PWM_SCALE();
    return r.pwm_scale_auto;
    // Not two's complement? 9nth bit determines sign
    /*
    uint32_t d = PWM_SCALE();
    int16_t response = (d>>PWM_SCALE_AUTO_bp)&0xFF;
    if (((d&PWM_SCALE_AUTO_bm) >> 24) & 0x1) return -response;
    else return response;
    */
}

// R: PWM_AUTO
uint32_t TMC2208Stepper::PWM_AUTO() {
    return read(PWM_AUTO_t::address);
}
uint8_t TMC2208Stepper::pwm_ofs_auto()  { PWM_AUTO_t r{0}; r.sr = PWM_AUTO(); return r.pwm_ofs_auto; }
uint8_t TMC2208Stepper::pwm_grad_auto() { PWM_AUTO_t r{0}; r.sr = PWM_AUTO(); return r.pwm_grad_auto; }