Follow up from Pt 2: Wire Up and Comms Analysis:

 

I don't have good news yet. Even though the data I send looks like the data I captured from the evaluation kit, I haven't been able to get an answer from the TMC2300.

Because I've made decent progress in the firmware, I'm going to document these changes here, and show what part of the communication works.

I don't know the reason yet. It may be a timing issue (I should implement a timeout), an understanding issue, the Hercules not dealing well with the 1-wire UART (although in that case I'd expect to catch data on my analyser)...

 

 

I'm road testing Trinamic TMC2300 EVAL KIT (2-PH Stepper Motor). It's their latest design that targets battery powered devices.

In this post: part 2 of a custom program to control the driver over UART.

This series of posts is an attempt to use the TMC2300 in a real design. I'm using a Hercules microcontroller in stead of the Landungsbrücke to control a motor. Firmware is in C++

 

Datagram class for TMC2300

 

The TMCLDatagram class has changed. It's now fit for TMC2300 UART datagrams instead of TMCL language datagrams. I should have changed the class name too, in hindsight.

I've taken over the following functionality from Trinamic's libraries:

the writeField() and readField() mechanisms, used to retrieve parts of a register.

the calcCRC() logic. I originally used the one from the datasheet but got wrong CRC values with that one.

 

class TMCLDatagram {
public:
  TMCLDatagram(uint8_t address);
  virtual ~TMCLDatagram();
  virtual void init(CommInit *commInit) = 0;
  virtual void open() = 0;
  virtual void close() = 0;
  virtual void sendCmd(uint8_t reg, uint32_t value);
  virtual uint32_t readCmd(uint8_t reg);
  virtual void writeField(uint8_t reg, uint32_t mask, uint8_t shift, uint32_t value);
  virtual uint32_t readField(uint8_t reg, uint32_t mask, uint8_t shift);
private:
  uint8_t _address;
  static uint8_t calcCRC(uint8_t *buffer, uint32_t len);
protected:
  virtual void physicalSend(uint8_t *txBuffer, uint32_t len) = 0;
  virtual uint32_t physicalRead(uint8_t *txBuffer, uint32_t len) = 0;
};

 

Highlight of the changes (CrC calculation is moved to it's own class):

define TMC_WRITE_BIT 0x80
#define TMC_ADDRESS_MASK 0x7F
// Macro to remove write bit for shadow register array access
#define TMC_ADDRESS(x) ((x) & (TMC_ADDRESS_MASK))

// Generic mask/shift macros
#define FIELD_GET(data, mask, shift) \
  (((data) & (mask)) >> (shift))
#define FIELD_SET(data, mask, shift, value) \
  (((data) & (~(mask))) | (((value) << (shift)) & (mask)))

TMCLDatagram::TMCLDatagram(uint8_t address) : _address(address) {
  CRC::tmc_fillCRC8Table(0x07, true);
}

TMCLDatagram::~TMCLDatagram() {
}

void TMCLDatagram::sendCmd(uint8_t reg, uint32_t value) {

 uint8_t txBuffer[8];
  txBuffer[0] = 0x05;
  txBuffer[1] = _address;
  txBuffer[2] = reg | 0x80;
  txBuffer[3] = value >> 24;
  txBuffer[4] = value >> 16;
  txBuffer[5] = value >> 8;
  txBuffer[6] = value & 0xff;
  txBuffer[7] = calcCRC(txBuffer, 7);
  physicalSend(txBuffer, 8);
}


uint32_t TMCLDatagram::readCmd( uint8_t reg) {
  uint8_t rxBuffer[8];
  rxBuffer[0] = 0x05;
  rxBuffer[1] = _address;
  rxBuffer[2] = TMC_ADDRESS(reg);
  rxBuffer[3] = calcCRC(rxBuffer, 3);
  physicalSend(rxBuffer, 4);
  physicalRead(rxBuffer, 8);

  // Byte 0: Sync nibble correct?
  if (rxBuffer[0] != 0x05) {
    return 0;
  }
  // Byte 1: Master address correct?
  if (rxBuffer[1] != 0xFF) {
    return 0;
  }
  // Byte 2: Address correct?
  if (rxBuffer[2] != reg) {
    return 0;
  }
  // Byte 7: CRC correct?
  if (rxBuffer[7] != calcCRC(rxBuffer, 7)) {
    return 0;
  }
  return (rxBuffer[3] << 24) | (rxBuffer[4] << 16) | (rxBuffer[5] << 8) | rxBuffer[6];
}


void TMCLDatagram::writeField(uint8_t reg, uint32_t mask, uint8_t shift, uint32_t value) {
  sendCmd(reg, FIELD_SET(readCmd(reg), mask, shift, value));
}


uint32_t TMCLDatagram::readField(uint8_t reg, uint32_t mask, uint8_t shift) {
  return FIELD_GET(readCmd(reg), mask, shift);
}

uint8_t TMCLDatagram::calcCRC(uint8_t *buffer, uint32_t len)
{
  uint32 crc = CRC::tmc_CRC8(buffer, len);
  return crc;
}

 

 

CRC Lookup

 

I originally used the calculations from the datasheet. I've switched to the algorithm of Trianimic's Github becaue the datasheet one seems wrong (or I interpret it wrong).

 

Header:

class CRC {
public:
  CRC();
  virtual ~CRC();
  static uint8_t tmc_fillCRC8Table(uint8_t polynomial, bool isReflected);
  static uint8_t tmc_CRC8(uint8_t *data, uint32_t bytes);
private:
  static uint8_t tmc_tableGetPolynomial();
  static bool  tmc_tableIsReflected();
  static uint8_t flipByte(uint8_t value);
  static uint32_t flipBitsInBytes(uint32_t value);
};

 

Implementation:

typedef struct {
  uint8_t table[256];
  uint8_t polynomial;
  bool isReflected;
} CRCTypeDef;

CRCTypeDef CRCTable; // I don't lik the static table here. Let's refactor later ...

CRC::CRC() {
}

CRC::~CRC() {
}
// ...
uint8_t CRC::tmc_fillCRC8Table(uint8_t polynomial, bool isReflected)
{
  uint32_t CRCdata;
  // Helper pointer for traversing the result table
  uint8_t *table;

  CRCTable.polynomial   = polynomial;
  CRCTable.isReflected  = isReflected;
  table = &CRCTable.table[0];


  // Extend the polynomial to correct byte MSBs shifting into next bytes
  uint32_t poly = (uint32_t) polynomial | 0x0100;


  // Iterate over all 256 possible uint8_t values, compressed into a uint32_t (see detailed explanation above)
  uint32_t i;
  for(i = 0x03020100; i != 0x04030200; i+=0x04040404)
  {
    // For reflected table: Flip the bits of each input byte
    CRCdata = (isReflected)? flipBitsInBytes(i) : i;

    // Iterate over 8 Bits
    int j;
    for(j = 0; j < 8; j++)
    {
      // Store value of soon-to-be shifted out byte
      uint8_t isMSBSet = (CRCdata & 0x80000000)? 1:0;

      // CRC Shift
      CRCdata <<= 1;

      // XOR the bytes when required, lowest to highest
      CRCdata ^= (CRCdata & 0x00000100)? (poly      ) : 0;
      CRCdata ^= (CRCdata & 0x00010000)? (poly << 8 ) : 0;
      CRCdata ^= (CRCdata & 0x01000000)? (poly << 16) : 0;
      CRCdata ^= (isMSBSet)?             (poly << 24) : 0;
    }

    // For reflected table: Flip the bits of each output byte
    CRCdata = (isReflected)? flipBitsInBytes(CRCdata) : CRCdata;
    // Store the CRC result bytes in the table array
    *table++ = (uint8_t) CRCdata;
    CRCdata >>= 8;
    *table++ = (uint8_t) CRCdata;
    CRCdata >>= 8;
    *table++ = (uint8_t) CRCdata;
    CRCdata >>= 8;
    *table++ = (uint8_t) CRCdata;
  }

  return 1;
}

uint8_t CRC::tmc_CRC8(uint8_t *data, uint32_t bytes)
{
  uint8_t result = 0;
  uint8_t *table;

  table = &CRCTable.table[0];

  while(bytes--)
    result = table[result ^ *data++];

  return (CRCTable.isReflected)? flipByte(result) : result;
}

uint8_t CRC::tmc_tableGetPolynomial()
{
  return CRCTable.polynomial;
}

bool CRC::tmc_tableIsReflected()
{
  return CRCTable.isReflected;
}


// Helper functions
uint8_t CRC::flipByte(uint8_t value)
{
  // swap odd and even bits
  value = ((value >> 1) & 0x55) | ((value & 0x55) << 1);
  // swap consecutive pairs
  value = ((value >> 2) & 0x33) | ((value & 0x33) << 2);
  // swap nibbles ...
  value = ((value >> 4) & 0x0F) | ((value & 0x0F) << 4);

  return value;
}

uint32_t CRC::flipBitsInBytes(uint32_t value)
{
  // swap odd and even bits
  value = ((value >> 1) & 0x55555555) | ((value & 0x55555555) << 1);
  // swap consecutive pairs
  value = ((value >> 2) & 0x33333333) | ((value & 0x33333333) << 2);
  // swap nibbles ...
  value = ((value >> 4) & 0x0F0F0F0F) | ((value & 0x0F0F0F0F) << 4);

  return value;
}

 

Test Bed Code

 

The main program is currently just trying to replicate the Evaluation kit's first 3 dialogs:

  gioInit();
  vin = 0;
  en = 0;
  sciInit();

  HerculesSciInit initStruct;
  initStruct._sci = scilinREG;

  datagram.init(&initStruct);
  vin = 1;
  wait(0xFFF);
  en = 1;
  wait(0xFFF);

  uint32_t retVal;
  datagram.sendCmd(TMC2300_GSTAT , 0x00000000 );
  wait(0xFFF);

  retVal = datagram.readCmd(TMC2300_IOIN);
  wait(0xFFF);

  datagram.sendCmd(TMC2300_IHOLD_IRUN, 0x00070301);
  wait(0xFFF);

 

 

Results

 

  datagram.sendCmd(TMC2300_GSTAT , 0x00000000 );

 

This is ok. The same as thecapture from the IDE.

 

  retVal = datagram.readCmd(TMC2300_IOIN);

 

This one sends the query but I never get a reply:

Not sure what happens. If I see the second communication from the IDE in my previous post, it also seems that this part doesn't send a reply.

Maybe the IC not sending a reply is a normal thing at this point?

 

I'll need more time... But the weekend's over. Time to clean up the desk and transform it into my office for the week ...

 

related blog
UART Interface
Evaluation Kit and IDE Functionality, basic functionality
C++ Custom Firmware Pt 1: Datagram Classes
C++ Custom Firmware Pt 2: Wire Up, Test and Comms Analysis
C++ Custom Firmware Pt 3: Try to Replicate Evaluation Kit Conversation
Trinamic TMC2300 EVAL KIT (2-PH Stepper Motor) - Review