CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Porting stepper motor library for TMC2209 driver
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
WrySun



Joined: 03 Jan 2023
Posts: 14

View user's profile Send private message

Porting stepper motor library for TMC2209 driver
PostPosted: Tue Jan 03, 2023 3:41 pm     Reply with quote

I'm trying to port the following library to use a TMC2209 stepper motor driver through UART (not step/dir pins):
https://github.com/terjeio/Trinamic-library

As far as I can tell there is currently no CCS library for the TMC2209 driver (or similar drivers like TMC2208, TMC2130, TMC2660 or A4988), and this Trinamic library is the only C library available for the TMC drivers.

The first issue I run into trying to #include tmc2209.h with CCS 5.0 is a "number of bits out of range" error. It seems that CCS expects the bit fields to be aligned to byte boundaries. Is there any workaround for this issue?

Code:
typedef union {
    uint32_t value;
    struct {
        uint32_t
        fclktrim  :4,
        reserved1 :3,
        ottrim    :2, // error: number of bits is out of range
        reserved :23;
    };
} TMC2209_factory_conf_reg_t;


There are lots and lots of typedefs like this in the tmc2209.h file to conveniently read the configuration registers of the driver, but most of them fail to compile because of this issue.
temtronic



Joined: 01 Jul 2010
Posts: 9222
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Jan 03, 2023 4:02 pm     Reply with quote

my comment.

Yes, could be byte boundary issue.
the third item, would have to 'span' 2 adjacent bytes, probably not allowed.
Just use 'dummy items' with # of bits to make 8.

fclktrim :4,
reserved1 :3,
dummy1 :1, //8th bit needed to make a byte
ortrim :2,
reserved :22;

be sure to reduce the 'reserved' item bits, by any 'dummyx' bits !

Now as I type this, the last reserved is 22 bits and has to span 3 bytes BUT if the compiler stops/halts at the 1st error, you won't see it being wrong....

I've always 'aligned' 8 bits to make a byte, as it seemed 'logical'.


I'm sure others will know, heck it could be a compiler bug ??

Easy enough to change code and test !
WrySun



Joined: 03 Jan 2023
Posts: 14

View user's profile Send private message

PostPosted: Tue Jan 03, 2023 4:17 pm     Reply with quote

Thanks for the reply. The following works in this instance, padding the struct into even bytes:

Code:
uint32_t
fclktrim  :4,
reserved1 :3,
ottrim_lo :1,
ottrim_hi :1,
reserved2 :7,
reserved3 :8,
reserved4 :8;


It's not as convenient to read the bit fields, but it's doable. I'll rewrite the structs and check what issue pops up next.
Ttelmah



Joined: 11 Mar 2010
Posts: 19503

View user's profile Send private message

PostPosted: Wed Jan 04, 2023 1:46 am     Reply with quote

The register definition as shown is wrong!...

The FCLKTRIM value is actually five bits not four.

Every bit in the register does actually have a meaning. Why not just
make your structure correctly name them all?. Most of the values
(such as FCLKTRIM), are factory set values, but a few are useful for
the driver to know.

I played with this a couple of years ago, and since I set my structure
up to reflect every bit, I didn't have any alignment problems.
WrySun



Joined: 03 Jan 2023
Posts: 14

View user's profile Send private message

PostPosted: Wed Jan 04, 2023 2:21 am     Reply with quote

Checking the TMC2209 datasheet, I see that you are absolutely right. The FCLKTRIM is 5 bits, not 4. Seems I can't trust this library to have everything right. Thanks for the insight.
WrySun



Joined: 03 Jan 2023
Posts: 14

View user's profile Send private message

PostPosted: Thu Jan 12, 2023 11:17 am     Reply with quote

Some success. I'm now able to compile the C library for the TMC2209 driver with CCS. The library does not include any documentation, comments or example code unfortunately.

With my current set up I can rotate the motor fine with the STEP/DIR pins. I'm now trying to rotate the motor via UART, by writing a velocity value to the VACTUAL registry. This is the simplest way I could think of to test the UART communication.

I have my transmit pin C6 connected to RX pin on the TMC2209 module, and the receive pin C7 connected to TX on the module. MS1 and MS2 pins are grounded, so address of driver becomes 0.

Here is the (simplified) code I'm using to send the stream and write to the VACTUAL registry.

Code:
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, bits=8, stream=PORT1)

//...

// Start rotating the motor (by writing a velocity value to VACTUAL registry)
// Based on TMC2209_WriteRegister() in tmc2209.c
bool TMC2209_Rotate()
{
  TMC_uart_write_datagram_t datagram; // struct in common.h made to simplify things
  datagram.msg.sync = 0x05;           // initial bits used by driver to calibrate baud rate
  datagram.msg.slave = 0;             // MS1 and MS2 pins are LOW, so address is 0
  datagram.msg.addr.value = TMC2209Reg_VACTUAL; // Address for VACTUAL registry (0x22) used to move motor by UART control (datasheet p25)
  datagram.msg.addr.write = 1;                  // this is a "write" not a "read" (last bit of "value" field)
  datagram.msg.payload.value = 100;           // velocity, microsteps/time
  byteswap(datagram.msg.payload.data);          // reverse order of these 4 bytes

  calcCRC(datagram.data, sizeof(TMC_uart_write_datagram_t)); // populate checksum field

  // send write data as an UART string, as specified in TMC2209 datasheet p15
  fprintf(PORT1,"%d%d%d%d%d%d%d%d",
        datagram.msg.sync,
        datagram.msg.slave,
        datagram.msg.addr.value,
        datagram.msg.payload.data[0],
        datagram.msg.payload.data[1],
        datagram.msg.payload.data[2],
        datagram.msg.payload.data[3],
        datagram.msg.crc);
}

void main(void)
{
  //... (setup stuff)
  TMC2209_t* driver;
  TMC2209_SetDefaults(driver); // populate driver struct fields
  TMC2209_Init(driver);        // write defaults to TMC2209 module
  TMC2209_Rotate();            // let's go!
  //...
}


No rotation occurs when I send this stream so I'm doing something wrong, but not sure what...
temtronic



Joined: 01 Jul 2010
Posts: 9222
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Jan 12, 2023 12:10 pm     Reply with quote

Normally an 'RS-232' communication network will have TTL>RS-232 and RS232-TTL interface devices (MAX232 is one such device.

I had a very quick look at the tmc2209 datasheet and they connect the device directly to the microcomputer.

I suggest adding the 'invert' option to the USE RS232(.....invert .....),recompile and test. This may work, no promises though.

If you have an oscilloscope, attach to the serial line,to confirm th ePIC is transmitting data.
Ttelmah



Joined: 11 Mar 2010
Posts: 19503

View user's profile Send private message

PostPosted: Fri Jan 13, 2023 1:54 am     Reply with quote

The big problem is he is sending the numbers as decimal ASCII. The chip
expects the numbers as binary. %d codes a byte as ASCII. %c sends the
byte as a 'character' which is what the chip requires......

fprintf(PORT1,"%c%c%c%c%c%c%c%c",

The UART is for direct connection to a micro. It says:
Quote:

The UART line must be logic high during idle state.


Which means it does not want the invert option.
WrySun



Joined: 03 Jan 2023
Posts: 14

View user's profile Send private message

PostPosted: Fri Jan 13, 2023 3:39 am     Reply with quote

Thank you so very much to you both! Ttelmah spotted my mistake and the motor started spinning as soon as I changed to %c. You're an absolute lifesaver. Finally some progress.
Ttelmah



Joined: 11 Mar 2010
Posts: 19503

View user's profile Send private message

PostPosted: Fri Jan 13, 2023 7:00 am     Reply with quote

Brilliant news.
Touch wood you can now progress further.
Have you built the circuitry to allow you to read stuff from the chip as
well as write?. All you need for a single chip, is a 1K resistor between TX
and RX and RX connected to the controller. Your code then has to handle
that it will receive every character you send. The great thing is that you
can then read stuff from the chip as well.
That the wake up is working tells you the checksum calculation is right
and should allow you to start some more powerful controls. Very Happy
WrySun



Joined: 03 Jan 2023
Posts: 14

View user's profile Send private message

PostPosted: Mon Jan 16, 2023 1:40 pm     Reply with quote

I've experimented with all of the write settings of the TMC2209. Even got CoolStep and StallGuard working which are really useful features that I didn't have on the L6470 driver I used before this one.

I cannot for the life of me get reading working. The TMC2209 module I'm using has the 1k ohm resistor integrated and I measure this resistance between the TX and RX pins of the module.

The read function I'm using is very similar to the write function, just that the read request datagram is 4 bytes instead of 8 since it has no data, as per page 16 of the datasheet. To simplify things I've increased the serial buffer size to 12 bytes.

Code:
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, bits=8, stream=PORT1, RECEIVE_BUFFER=12)

// ...

void readDatagram()
{   
  TMC_uart_read_datagram_t datagram;
  uint8_t b[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
  uint8_t count = 0;
 
  datagram.msg.sync = 0x05; // initial bits used by driver to calibrate baud rate
  datagram.msg.slave = 0; // MS1 and MS2 pins are LOW, so address is 0
  datagram.msg.addr.value = TMC2209Reg_GCONF; // address for GCONF registry (0x00) for general config
  datagram.msg.addr.write = 0; // read=0, write=1 (last bit of "value" field)
  datagram.msg.crc = calcCRC(datagram.data, sizeof(TMC_uart_read_datagram_t)); // set checksum field

  // clear receive buffer
  while (kbhit(PORT1)) { fgetc(PORT1); count++; }

  // send read datagram
  fprintf(PORT1, "%c%c%c%c", datagram.msg.sync, datagram.msg.slave, datagram.msg.addr.value, datagram.msg.crc);

  delay_ms(10); // wait for echo and reply bytes

  // read all buffered bytes
  for(int i = 0; i < 12; i++) {
    if (!kbhit(PORT1)) { break; }
    b[i] = fgetc(PORT1);
  }
 
  sprintf(out_data,"Count: %d, Bytes: %d %d %d %d %d %d %d %d %d %d %d %d", count, b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7],b[8],b[9],b[10],b[11]);
}


Output I get is "Count: 0, Bytes: 5 0 2 -113 0 0 0 0 0 0 0 0" so I am receiving the 4 echo bytes, just not the 8 reply bytes.

I've tried connecting my RX pin C7 directly to the TMC2209 module's RX via a 1k ohm resistor, but it gives the same result. I've also tried reading other addresses without any different result. The CRC function works for the write function and is provided in the datasheet so that shouldn't be the issue. I don't have an oscilloscope so can't see the bits.
Ttelmah



Joined: 11 Mar 2010
Posts: 19503

View user's profile Send private message

PostPosted: Tue Jan 17, 2023 11:06 am     Reply with quote

Your problem is this:

delay_ms(10); // wait for echo and reply bytes

Remember the physical UART only has a couple of characters of buffer.
You are not reading, so everything sent to you, except the last
couple of characters has now been lost!.....

You need to modify the '#use rs232, to enable interrupt driven serial
buffering. Make the buffer large enough to cope with receiving the bytes
being sent as well, and then things have a chance of working.
WrySun



Joined: 03 Jan 2023
Posts: 14

View user's profile Send private message

PostPosted: Wed Jan 18, 2023 2:41 am     Reply with quote

I've enabled interrupts to get the RECEIVE_BUFFER=12 working. It is able to accurately buffer 12 bytes. If a 13th arrives the buffer is reset to 0 and starts over.

Code:
enable_interrupts(GLOBAL);
enable_interrupts(INT_RDA);


I've tested reading the bytes as they come in one at a time, but it gives me the same result with the 4 echo bytes.

Code:
uint16_t delay = 0;
uint8_t index = 0;

while (delay < 40000) {
  if (kbhit(PORT1)) {
    b[index] = fgetc(PORT1);
    index++;
  }
  delay_us(1);
  delay++;
}


It seems the driver isn't responding to the datagram. Looking at the sent datagram in binary it is:
00000101 00000000 00000000 01001000
With sync=5, slave address=0, register address+read bit=0 and CRC=72. CRC calculation is taken from the datasheet page 17 so that should be correct. Also, it's working properly for writing data to the driver so I don't think that's the issue.
temtronic



Joined: 01 Jul 2010
Posts: 9222
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Wed Jan 18, 2023 9:09 am     Reply with quote

sigh, it's sooo close to working....
I wonder if you should

send to chip
clear the buffer
wait 9ms
then receive the incoming data

??

my thinking is that since Tx and Rx are tied together with the 1K, Tx data gets sent to the Rx part of the UART.

I'm probably wrong but should be easy to prove.

also, wonder if there's any other users of the chip that READ it ? I see lots SENDING to it......
Ttelmah



Joined: 11 Mar 2010
Posts: 19503

View user's profile Send private message

PostPosted: Thu Jan 19, 2023 1:54 am     Reply with quote

I did manage to read it.
Was not using a PIC for this, but the way it was done was that the receive
interrupt routine, was given the count of sent bytes, and it simply read
and threw away each 'sent' byte. What was left was the stuff being
returned. The main code only received these bytes.
Reading is not needed for 90% of stuff. However there are some useful
things like the temperature and load statuses. Also you could read what
current was actually being delivered.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group