UART controlled stepper drivers and linuxcnc

More
19 Aug 2022 16:33 #250080 by maarten12100
For the past year or so I have been using linuxcnc with the parallel port and this has worked very well for me. The topic is more of a curiosity rather than something I have a use/need for, I would love to hear the opinion of others on this way of controlling stepper drivers and how they would approach it and what they would specifically look out for.

Recently I came accross the trinamic tmc2209 stepper driver, this stepper driver can use a UART connection for motion control and has an internal step generator. Simply write a specific register and it starts generating step pulses internally that feed the stepper driver.
Datasheet

These drivers are suitable for small to medium sized stepper motors, are low cost (less than 2 euros in bulk), handle step generation and don't require much aditional circuitry to work with a RS232 port. Remarkably the max step generation rate according to the datasheet is 1/512 of the baseclock (12 MHz internal oscilator) or 1/2 the baseclock for full and 1/256th microsteps respectively, which is 23.4 kHz and 6 MHz respectively.
Going from RS232 to UART (inversion and level shift essentially) can be done with an IC like MAX232 (less than a euro). Some other components and opperations are needed namely a pcb, some passives, wiring and assembly. Optionaly for more robustness external sense resistor, external crystal, RS485 ICs and a heatsink.

Assuming you have a RS232 port (PCIe adapters exist and are about the same price as parallel port adapters) a driver could cost as little as 5 euros. Without additional components 4 drivers can be controlled this way.
USB to RS232 or USB to TTL adapters would not be suitable and neither would using the a python component unless you are ok with going slow (or you might lose track of position) and choppy (as you can only update the velocity every 10s of microseconds).
The limiting factor here is the register (MSCNT) that keeps track of the current step, at high step rates it might overflow twice between two consecutive read operations.Obviously you wouldn't want to go this route but it's interesting to note that given you know the previous MSCNT value, the current step velocity (VACTUAL register) and the timestamp the MSCNT register is read you could detect the number of rollover events, though the timestamp accuracy depends on the jitter in USB and or pyserial.

What we do want to do is drive the RS232 port directly and while there is no component that does this for us (SERPORT's READ function read the status of the pins from the registers not the RX) we could just use the same functions it uses internally namely rtapi_outb and rtapi_inb. The documentation says it can't run in userspace guess it needs the lower level access the userspace components don't have. It also needs to be reserved.

Just pass this function the I/O address of the serial port or any of the associated register adresses and you can control that port.
A reference on what the various registers do/hold can be found here . Some setup is required to configure the interface prior to communcation (baud rate, data bits, parity bit) though the tmc2209 will adopt the master's baud rate.

After the communication with the driver is working a component similar to stepgen can be made. This takes the input from the motion planer (commanded position, requested velocity), max velocity constant, max acceleration constant as well as the scale constant (steps per length unit). And that should be all information it needs to generate the velocity ramp profiles it would then provide position feedback to the motion planer based on the MSCNT register changes.

A side note to this control scheme is that it doesn't naturally play nicely with the emergency stop like step signals do once a velocity command is send the motor will not stop untill a zero velocity command is send. So an extra input to the component that checks the estop and sends the stop condition to the driver is probably a good idea.

That's how I would approach this, another low to medium cost control scheme besides MESA cards, Remora-eth and the parallel port would be pretty interesting.

Please Log in or Create an account to join the conversation.

More
20 Aug 2022 14:25 #250103 by TOLP2
Interesting approach. Some questions to help you to design the driver for this:
- I don't think you want to have the communication in the base-thread. Therefore it should be in the servo thread which might have a period of 1 ms. How are you going to make the trapezoidal speed profiles. Can the stepper driver handle these. Otherwise you can accept jerky acceleration (speed increase) at each period.
- you might consider connecting the E-stop to the enable of the stepper driver for safety.
- I only do have one serial port. How to connect multiple drives in this case?

The process of the hal-file should be
- read from the device (gives the position)
- do motion planning
- write to the device.

This means that ideally you should create separate read and write functions.

For creating the driver,  I can assist if you want. 

(off-topic: another cheap alternative are the diverse drivers which have been / are developed for Colorlight Fpga cards, see thread Here )

Please Log in or Create an account to join the conversation.

More
22 Aug 2022 13:02 - 22 Aug 2022 13:04 #250200 by maarten12100
I agree that the servo thread is preferable or a custom thread with floating point math enabled. Perhaps the read and write functions are better of existing on a faster non FP thread.

The acceleration is jerky as the step generator doesn't smoothly transitions between step velocities. The update rate is thus limited by the register write time. The write datastructure is 8 bytes each with a start and stop bit, 80 bits at 115.2 kbps requires about 0.7 ms, read requests are half that. Depending on the PC serial port communications at 4x higher baud rate may be possible.
I don't have a feeling for what numerical value would result in jerky acceleration if I had to guess I would say increasing near or more than the maximum starting speed of the motor would certainly be jerky. Picking a reasonably high values for the acceleration suchs as 1000 rotations/s^2
We would get an abrupt velocity increase of 0.7 rotations/s (42 rpm) which is about 1/10th of the ~400 rpm maximum starting velocity
I came across. Based on this I expect acceleration to be a little jerky at high acceleration and smooth at medium acceleration (200 rotations/s^2).

As for connecting multiple drives you can address 4 of them per UART trough an address in the datagram, 2 pins on the IC set the slave address. It's a bit of a shame they reserved 8 bits for the slave address and only use 2 of them if they had added more selection pins a large number of slaves per UART would've been possible. When you address more than 1 per UART though you are sharing the limited baud rate which will make acceleration less smooth and position data less frequent.

As for motion planning I intend to leave it all to standard motion module motmod and just feed that the position feedback just like it is now with stepgen. The nice part is that the pos cmd (joint position command) output from motmod to stepgen is already fully conditioned to take into account maximum acceleration, requested velocity and maximum velocity. By taking the difference between two consecutive commanded positions you can get the velocity you need to write, motmod knows where you are based on the position feedback. At least this is my understanding based on looking at the pos cmd output during some movements.

I have written components in the past which were manipulating the adaptive feed input pin based on supply current which I was reading with MB2Hal from a power supply. This was used for ECM machining experiments where the amount of machining is closely tied to the current passed. I haven't done anything in terms of serial communication and linuxcnc but I don't expect that many issues, I will take a pc with serial port hook up the TX line to an osciliscope and see if I can output succesfully. I hope modern PC serial ports actually use pretty high frequency (10MHz instead of 1.8 for example) oscilator so they can be configured to run faster than 115.2kbps, but I'll have to see.

(I looked at the Colorful driver I had no idea such an affordable of the shelf ethernet based board existed.)
Last edit: 22 Aug 2022 13:04 by maarten12100.

Please Log in or Create an account to join the conversation.

More
23 Aug 2022 08:37 #250245 by rodw
I think you are wasting your time. I looked at the manual.  My take on what it says is the UART is for controlling the internal stepgen for industrial applications. But there is a mode (option 3) that combines the UART for control with step and direction pins.

CNC motion needs real time control which the serial port can't provide. It requires step and direction.

CNC is a very small market vs industrial applications.

Please Log in or Create an account to join the conversation.

More
25 Aug 2022 13:55 #250400 by Tinine
Not sure if these are of any interest but I have used them elsewhere for coordinated motion. I happen to be a closed-loop servo guy and so I used the servo chip but they also have a stepper version.
They have buffers and a trapezoidal profile generator. Maybe have a look at the datasheets.

Regards,

Craig

Please Log in or Create an account to join the conversation.

More
25 Aug 2022 14:27 #250401 by maarten12100
Most implementations are in the 3D printers where it is used as a drop in replacement  and it's required the driver works with step and dir based controller boards. The uart interface however does give you access to the two functions you would need to move and know where you are, VACTUAL is a signed register that holds the velocity setting and MSCNT holds the current step data.

As for the realtime concerns they do pose a challenge; clearly the serial or parallel port being used to send data is not as direct as sending pulses (writing two bytes to the the parallel port data register). For the parallel port these two bytes can flip 8 pins twice at the desired time, for non concurrent flips more writes are needed in which only a couple bits in the byte is changed. That's briefly the write port function in the parport.c file.

Actually the idea here is not really different compared to MESA ethernet boards since the information that is being send is just a requested step velocity, just the transport layer (UART instead of ethernet) and stepgen hardware (asic instead of fpga) differ. Just like the MESA there is no command buffering.
If the velocity settings can be transfered and the step position can be retrieved faster than the 1 ms basethead period the "performance" except for acceleration (+/-) should be pretty good. I don't see how it would perform worse than say a MESA card if that condition is met and it could certainly do higher internal stepgen than trough the parallel port.

That's not a trivial data transfer rate to achieve even communicating with just 1 drive. To make matters a little worse the tmc2209 has just 1 pin uart (like the bidirectional pins in EPP/ECP enabled parallel ports). This means At 115.2 kbps you need to Request MSCNT, send VACTUAL and Receive MSCNT which add up to 0.35 + 0.7 + 0.35 ms. Viability is all down to how fast the communication can be done on the PC side, the datasheet specifies the maximal baudrate as fclk/16 and the clock no higher than 16 MHz (yet they mention 20 MHz later in their datasheet) giving 1000 kbps.

which type of port is used isn't that relevant both serial and parallel port can do it. The parallel port already has the logic level in the right range and could be directly connected (Vio pin @3.3V gives a 0.7*3.3 = 2.3V logic high) but can it go fast enough.
 

Please Log in or Create an account to join the conversation.

More
26 Aug 2022 17:20 - 26 Aug 2022 17:23 #250537 by maarten12100
I ran some tests on a CH382L based pci-e to parallel port card. First I had to find the port base address trough "dmesg | grep par" they put it at an uncommon address namely 0xe100. The port was reported as PCSPP, Tristate. The rest of the system was a i5 6400t based system running linuxmint 19.3 and the Preempt-RT 4.9.0.0.13 kernel.

A small component was made that uses the rtapi_outb function to toggle the parallel port to see what this chip could do. An osciloscope was hooked up to one of the datalines (doesn't matter which one as I was writting 11111111 and 00000000).

Without any delays between two consecutive writes the parallel port data pins were toggling in about 1.1 us or 909 kHz. That's a lot better than I was expecting since I saw the chip reporting SPP earlier. However on long sequences there would sometimes appear a large delay in the middle of a pulse train of about 20 us (something is interrupting the code in the middle of waiting before sending the next bit)
I tried doing some proper timing and found that the rtapi_get_time function is way too slow taking up 4.5 us instead I had to use rtapi_get_clock and for testing I hardcoded the delay based on the number of cycles in the desired delay time.

The seeminlgy stochastic interrupt events are a big problem as they will ruin an entire transmission. As for their occurance on my system I saw about 1 in 4 10 byte transmissions get corrupted this way. Preferably I would like to fix it in a way that doesn't require workarounds outside linuxcnc. I did try a couple things but they didn't seem to make a difference though I saw the time these interrupts take shrink. First I tried moving the component to the fastest thread I had which is the 100 us base thread. In the hope that it was just the base thread interrupting the servo thread but that didn't make a difference, I could move it to a thread faster than the base thread so that it gets maximum priority but I doubt it would make a difference. And besides the transmissions take more time than the base thread can give, so unless I slow all the other threads it's pointless and bad practice.

I went into the bios and disabled all cores except one, speedstep and turboboost. This seemed to reduce the interrupt period to about 8 us which doesn't chnage that the transmission is ruined. I have not tried isolating some cpu cores to dedicate them to linuxcnc as that would be a pretty big change from the standard system setup but it might be necessary. Preferably I would just postpone interrupts during the ~1.5 us wait between port toggles but unlike on a baremetal microcontroller that option isn't available.
Last edit: 26 Aug 2022 17:23 by maarten12100.

Please Log in or Create an account to join the conversation.

More
27 Aug 2022 03:51 #250554 by rodw

Actually the idea here is not really different compared to MESA ethernet boards since the information that is being send is just a requested step velocity, just the transport layer (UART instead of ethernet) and stepgen hardware (asic instead of fpga) differ. Just like the MESA there is no command buffering.

 

Its totally different. Mesa just receives a frequency command and generates that frequency forever until told to change it.
Your drive I would expect needs to receive a waypoint.
909 khz is not helpful assuming a 1 kHz servo thread...
Its likely the lags are due to overrunnig your thread time period.

Please Log in or Create an account to join the conversation.

More
27 Aug 2022 14:31 #250577 by maarten12100
It has a step generator internally it's not taking waypoints or doing any part of the motion planning. And indeed it will keep generating at a certain rate set by the VACTUAL register until that value is changed, this is a signed value and the direction is also controlled this way. A somewhat problematic property in the case of a communication disruption. In the datasheet section 14 on page 64 has some information and a graphic on the step pulse generator.

Pushing out 1 bit (parallel port) or 1 byte (serial port) per itteration of even the base thread would be too slow, getting the timing right at a high rate of data transfer is only viable with small waiting periods so in a single itteration. This works fairly well but besides some small bookkeeping tasks cycles are wasted waiting.

The approach is like this:
component function is called 1000 times per second from the servo-thread

component
1. Calculate the required step velocity, generate the datagrams, calculate the CRC.
2. Send current step read request (the arrival of the response is delayed by a certain number of bit times, set trough another register)
write a bit
wait
write a bit
repeat untill done sending all the bits
3. Send the step velocity
write a bit
wait
write a bit
repeat untill done sending all the bits
4. receive the requested current step
receive untill all bits are received or timeout
5. decode the current step information, check the checksum and use it as position feedback (how far have we moved)

This takes about 0.2 ms at 800 kbps.
This exludes the initialization section that will have to run once on startup.

I hooked up the parallel port to a cp2102 usb to ttl adapter and monitored it trough the serial monitor in the ardruino IDE (for some reason it accepts no higher than 115200 even though the cp2102 can do 921600 bps). I wrote 10 bits in order 0101010101 the dataformat is
| Start bit | B1 LSB | B2 | B3 | B4 | B5 | B6 | B7 | B8 MSB | Stop bit | what was written in normal order is thus 01010101 which is a 'U' in ascii. And without fail this is what was printed to the serial monitor. So at this 'low' bitrate with only 10 bits communication is solid. I will look for a different serial monitor that accepts higher baudrate and then try sending 80 bits at a time at for example 500 kbps or higher. If they come out consitently the same characters then I'm content with the communication.

I don't have any tmc2209 currently to test with so I will either lay out a board with jumpers and external crystal or buy the premade tmc2209 boards for 3D printers.

Please Log in or Create an account to join the conversation.

More
03 Sep 2022 19:30 #251128 by andypugh
I am going to disagree with RodW here and say that I think that this can be made to work. In fact I have a feeling that it is already working.

Is there any way to read back the step-counts from the drivers? The problem with sending a step-rate is that you don't actually know where the motor is unless you get feedback.

Use the parallel port driver as a model, but keep only the servo-thread part. Note that the base-thread part reports back how many steps have actually been made, to allow the servo-thread part of the driver to calculate the required step rate.

The Mesa drivers work very similarly, except that they send a step-rate to an FPGA. Again, it needs to have a feedback of the number of steps _actually_ made.

github.com/LinuxCNC/linuxcnc/blob/master...a-hostmot2/stepgen.c

There is a lot in there, but the "prepare_tram_write" is the bit that sends the step rate to the FPGA.

Realtime UART control _is_ possible. If I was doing it I would use the Mesa UARTs as they are _definitely_ realtime.
Sample UART driver:
github.com/LinuxCNC/linuxcnc/blob/master...ivers/mesa_uart.comp
The following user(s) said Thank You: tommylight

Please Log in or Create an account to join the conversation.

Time to create page: 0.104 seconds
Powered by Kunena Forum