gin66 / FastAccelStepper

A high speed stepper library for Atmega 168/328p (nano), Atmega32u4, Atmega 2560, ESP32, ESP32S2, ESP32S3, ESP32C3, ESP32C6 and Atmel SAM Due
MIT License
301 stars 70 forks source link

Direction pin optional? #142

Closed cranefist closed 2 years ago

cranefist commented 2 years ago

Hello

Can you clarify this? Could i somehow use the UART connection to change direction instead of the direction pin in this library?

As TMCStepper library allows me the change the direction via : driver.shaft(false/true)

It does seem to speak about using shift registers "With external callback on esp32 derivates, even shift register outputs can be used". And i'm using an ESP32.

This would fee up one pin.

Or does it just mean, that the library can operate without the direction pin?

gin66 commented 2 years ago

if the stepper shall run forward and backward, then a direction pin is required. The direction pin can be controlled by the application by any means (e.g. shift register or else). FastAccelStepper just need to know the external callback. Check #132 for reference

cranefist commented 2 years ago

Sorry i did not notice your response.

I tested this, but it seems setExternalCallForPin does not exists?

'class FastAccelStepper' has no member named 'setExternalCallForPin'

This is what i tried:

stepper->setDirectionPin(129);
stepper->setExternalCallForPin(exampleCallback);

Or is this so new that its only in the github? And i need to download that? ...I did download the master, and it still says the same.

Ok, I missed this part: I have made a mistake. The callback needs to be informed to the engine. So use instead engine->setExternalCallForPin(exampleCallback);

But this does not work either; base operand of '->' has non-pointer type 'FastAccelStepperEngine'

Other than that, i expect this works similarly to setExternalEnableCall that im already using?

So all i need is this, to change the direction via UART using TMCStepper librarys shaft(true/false) :

bool exampleCallback(uint8_t pin, uint8_t value) {
   driver.shaft(value);
   return value;
}

I expect that value is 1 or 0 and works as TRUE/FALSE bool?

gin66 commented 2 years ago

You are right. It should be engine.setExternalCallForPin(exampleCallback);

Your example callback looks good.

cranefist commented 2 years ago

I actually tested that also, it did not work either. undefined reference to FastAccelStepperEngine::setExternalCallForPin(bool (*)(unsigned char, unsigned char))'

gin66 commented 2 years ago

cool. The implementation was just missing. Having no test for a feature opens the door for failure…

Fix has been pushed to master. But still no test….

cranefist commented 2 years ago

Ok good, now it compiles.

This is a nice feature, as these modern TMC drivers use 1 wire UART. So now i only need 2 wires + power.

This will especially benefit if you have multiple drivers, as they are all connected to the same UART line. I'm only using one driver, but if you have like 5... and not needing separate DIR pins, that's quite a big plus.

Sadly i just blew up my last driver, need to wait for a replacement before i can confirm will it actually work :)

I also moved into using attachToPulseCounter, it seemed to work fine. Do i benefit anything from it? Is it more efficient way of counting steps? Im only doing short movements with no microstepping, so the 14bits is fine for me. And apparently you also added options for counting more steps if needed.

gin66 commented 2 years ago

attachToPulseCounter just gives access to real-time data instead of calling a lengthy API function. It is great for verifying having no pulse losses during development for esp32

So I wait for your confirmation. Eventually I will update the driver to use those callbacks as internal standard. This will reduce complexity by removing a couple of if(){} structures.

cranefist commented 2 years ago

I shall report back in about 1 month :) Developing stuff is fun when you order everything from china.

cranefist commented 2 years ago

One month later, as promised, does not seem to work. Motor just stops and sets hold current when it should change direction.

Starting shaft direction does not seem to matter, so if i set it to true or false in the beginning.

It does not ever arrive in the exampleCallback bool function, i placed some serial prints there and nothing.

gin66 commented 2 years ago

Pity. So the implementation is faulty and I need to debug it. Hopefully I can do it over the weekend.

cranefist commented 2 years ago

Let me know if you need me to test something, its worth implementing.

gin66 commented 2 years ago

I have tested with avr simulator and fixed a bug. Hopefully it works now for esp32, too. The version is 0.28.0

cranefist commented 2 years ago

It works, but it seems to be unstable.. sometimes it does not change the direction. And it seems to be really random when it suddenly does not change the direction.

I need to do some more testing on it, on my current test jig im just moving the motor, then stopping for 3 seconds change direction and return to 0. Ill try some more rapid direction changing to see if i can report back anything useful.

gin66 commented 2 years ago

Thanks for checking. I think, I see the issue in the esp32 code using mcpwm/pcnt. need to figure out a solution. The esp32 rmt looks better.

gin66 commented 2 years ago

github automatism is surprising….

Anyway, I hope the error has been fixed.

cranefist commented 2 years ago

Still the same problem. I placed a toggling boolean in the function, and it does do what its supposed to do. So your code works, mine does not.

Seems the UART command is not being registered always, i need to confirm that its been switched. The problem is probably because i'm reading via UART in my diagnostic printouts. And as those are running on different cores, they might try to do it at the same time.

If i understood correctly, if i return the opposite value in the function it then repeats it again? Until it returns the correct value?

So i tested this quite brute method, but this works. Probably not the best way, but at least it confirms it works reliably now.

bool DirectionChange(uint8_t pin, uint8_t value) {

motorDir=!motorDir;

do
{
driver.shaft(value);
vTaskDelay(1);
}
while (driver.shaft() != motorDir);

return value;
}

In most cases it takes 7ms to complete this when it gets through it with one loop, but worst case now is 45ms.

Also, i forgot to mention that i did not update the library. This was done without your previous changes using v0.28.0.

gin66 commented 2 years ago

yes, if the opposite value is returned, the driver will try again. The brute force method is problematic, if you run more than one motor, because this may cause another motor run out of stepper commands. So instead of the while loop, perhaps this would work:

bool DirectionChange(uint8_t pin, uint8_t value) {
   driver.shaft(value);
   return driver.shaft();
}
cranefist commented 2 years ago

Yes, that seems to be perfectly elegant solution. I was confused about the boolean uint8_t.. but apparently they can mix. Tried something similar, but with unnecessary complexity.

Thanks for the great work.. this seems to be now working perfectly. Also helps me to understand why you cant control this driver completely via UART. But this is the best compromise, only two wires just like without UART.

cranefist commented 2 years ago

One thing i actually just noticed, the pulse counter stopped working. This is still on the 0.28.0.

Also, about the pulse counter. I occasionally got like -2 from it, does this mean that i had lost two steps? I'm still a bit confused about the pulse counter and its benefit.

cranefist commented 2 years ago

There still seems to be times when it does not change the direction, it also now happens always the same way. Always in the same direction. When it should move forward (1->0), it moves backward (1->1). Never the other way around.

Your library works, i included a boolean to keep track of the value given to the function. It changes...

Hard to understand why it does not ever fail when moving from 0 to 1. Maybe failed driver.shaft() reading always results in 0?

So when the reading the function gives as return, fails and gives out 0. While actually driver.shaft() did not change and is still 1.

This did not happen with my brute force method. And that is puzzling also, as its basically the same loop.

I also switched to the latest master that included your last changes, just to see if it affected anything. No change.

gin66 commented 2 years ago

what is this driver ? any library ?

cranefist commented 2 years ago

This one: https://github.com/teemuatlut/TMCStepper

gin66 commented 2 years ago

After having a look at the implementation of that driver, I recommend to move the actual bit toggling into another task and not in scope of FastAccelStepper’s callback. This would mean, that the callback e.g. sends a task signal or writes a variable, which the other task will use as input for toggling the bit. As soon as this toggle process is successful, a volatile variable can be set, which the callback will read on the next loop of FastAccelStepper.

cranefist commented 2 years ago

I was testing something like that, i will experiment and try to find the best solution.

Thanks a lot for the help!

Your library works now, expect the pulse counter stopped working for some reason when using this direction function. But for me this issue is resolved!

gin66 commented 2 years ago

The attachable pulse counters are configured to read back the stepper pin and the direction pin. But this works only for hardware pins. As this is an add on, I have not added a check to prevent external direction pins to be used with pulse counter. eventually I can add this to the documentation.