element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Members
    Members
    • Benefits of Membership
    • Achievement Levels
    • Members Area
    • Personal Blogs
    • Feedback and Support
    • What's New on element14
  • Learn
    Learn
    • Learning Center
    • eBooks
    • STEM Academy
    • Webinars, Training and Events
    • More
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • More
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • More
  • Products
    Products
    • Arduino
    • Dev Tools
    • Manufacturers
    • Raspberry Pi
    • RoadTests & Reviews
    • Avnet Boards Community
    • More
  • Store
    Store
    • Visit Your Store
    • Choose Another Store
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
Motors and Drives
  • Technologies
  • More
Motors and Drives
Blog Trinamic Stepper Motor Controller TMC2300 - C++ Custom Firmware Pt 3: Try to Replicate Evaluation Kit Conversation
  • Blog
  • Forum
  • Documents
  • Events
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Motors and Drives requires membership for participation - click to join
Blog Post Actions
  • Subscribe by email
  • More
  • Cancel
  • Share
  • Subscribe by email
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 16 Feb 2020 6:59 PM Date Created
  • Views 409 views
  • Likes 4 likes
  • Comments 3 comments
  • tmc2300
  • stepper
Related
Recommended

Trinamic Stepper Motor Controller TMC2300 - C++ Custom Firmware Pt 3: Try to Replicate Evaluation Kit Conversation

Jan Cumps
Jan Cumps
16 Feb 2020

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
Anonymous

Top Comments

  • DAB
    DAB over 2 years ago +1

    Good post Jan.

     

    DAB

  • Jan Cumps
    Jan Cumps over 2 years ago +1

    Side track: I've prepared the board for power use measurement.

     

     

    When the motor is running, the power used by this IC will be in the noise.

    But if the design is going to be powered from batteries, it makes…

  • Jan Cumps
    Jan Cumps over 2 years ago in reply to Jan Cumps +1

    Customer action photos:

     

Parents
  • Jan Cumps
    Jan Cumps over 2 years ago

    Side track: I've prepared the board for power use measurement.

     

     

    When the motor is running, the power used by this IC will be in the noise.

    But if the design is going to be powered from batteries, it makes sense to see its power profile while doing nothing for long times.

     

     

    I've removed a 0 Ohm resistor in the current path (the one used to select 3.3 / 5 V operation) , and wired in a jumper.

    When the circuit is brought out of standby, the circuit has 2 10K resistors in parallel. That takes 660 µA right at the start.

    That's a fair part, given that I'm trying to measure a 1.5 mA current. But I am not keen on fiddling with the standby mechanism, and cutting a trace after R315 is not easy on a PCB with white solder mask.

    At least we know where 0.67 mA of the current is consumed ...

     

    • Cancel
    • Up +1 Down
    • Reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 2 years ago in reply to Jan Cumps

    Customer action photos:

     

    • Cancel
    • Up +1 Down
    • Reply
    • More
    • Cancel
Comment
  • Jan Cumps
    Jan Cumps over 2 years ago in reply to Jan Cumps

    Customer action photos:

     

    • Cancel
    • Up +1 Down
    • Reply
    • More
    • Cancel
Children
No Data
Element14

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2022 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • Facebook
  • Twitter
  • linkedin
  • YouTube