Very fast stepper motor drive while doing another high priority task

Hi,
I have to drive a stepper motor and a printhead that need to by perfectly synchronised.

The whole process is happening extremely fast. The stepper can rotate at speeds that require a clock/pulse signal up to every 40 us (yes micro seconds) - because it is driven in micro-stepping mode.

And the printhead needs to have the data shifted into it while the stepper rotates at those speeds. The printhead data loading can take up to 35 micro seconds every motor pulse.

Unfortunately the pins providing the pulse for the stepper are not PWM or output compare that I can run independently.

What is the best way to implement it in RTOS?

The processor is a PIC32 running at 100MHz. So my initial thought was to change the 1,000 Hz RTOS tick to 40 or even 20 micro seconds. That is a factor of 50 times faster. But by doing that a lot of time is wasted by the OS continually context switching and there might also be other potential RTOS issues I am not aware of.

Those two tasks (steppe and printhead data loading) are the top priority tasks (as far as the application is concerned). Apart from a couple of other interrupts processing just switch press detection for emergency.

What would you suggest?

Thank you as always! :slight_smile:

That speed work runs in a high priority interrupt. Maybe even above the priority that interacts with FreeRTOS to avoid being affected by critical sections. I wouldn’t even try to run that in a task.

Sounds like someone didn’t do enough homework on pin assignments.

1 Like

Thank you Richard,
are you suggesting that neither of the two “tasks” (printhead loading and stepper drive) are done via the OS?

Thank you

The ‘OS’ never does things like that. The OS just schedules tasks and provide services for communication between tasks and between tasks and some interrupt.

The actual ‘work’ that your program does, is in. your code. The work you are talking about, with this sort of tight tolerances, needs to be done with minimal overhead and interference, so is best done at a ‘bare metal’ level. Running the scheduler could easily have costs on the order of a micro-second on this system (that’s only 100 assembly instructions), so at a 40 microsecond period that is several percent. Using high priority timer interrupts to trigger this functionality seems to be the best bet. Depending on exactly how little jitter you need, you may need to put the task above the level that FreeRTOS blocks in critical sections.

FreeRTOS does have a design parameter that critical sections that keeps the length of its critical sections ‘short’ and bounded, but because of hardware and compiler variations, it can’t provide a number in absolute time. You could instrument the critical sections (setting high a port bit when entering, and clearing it when re-enabling interrupt) so you could trigger a scope on the bit to see what the worse case time you can measure.

If that time is short enough for you, then you just need to make that interrupt above any interrupt that might take long enough to execute to cause a problem, but still low enough that it can use FreeRTOS primitives to signal tasks. If it isn’t then you need to put it above the level that FreeRTOS masks, and the ISR needs to trigger some lower priority interrupt to signal the OS.

Thank you Richard once again for the detailed explanation. That helps a lot while learning and writing deliverable code at the same time. :slight_smile:

As Richard says, driving stepper motors is best done by an interrupt from a timer. For sending data to the print head, what is the protocol? Can you use DMA to send it?

I seriously doubt you will achieve what you are trying to do without redesigning the hardware so that the driver pins are connected to appropriate timer / stepper driver peripherals. Trying to fix this in software will only lead to pain and frustration and ultimately a poor solution.

You are going to need a different strategy than generating these signals in software. The PIC32 has DMA and numerous timers. The DMA can be used to copy RAM to an I/O port. This would allow you to prepare your pin patterns in the DMA buffer and use the timer to play the pattern to the pins at the high speed. The stepper pattern can be generated synchronously by a similar RAM pattern and DMA or perhaps by setting up a PWM. So long as the same clock drives both patterns (or PWM) they will remain in sync and can easily run at the high speeds. (of course you may still have pin drive issues as not all I/O pins can reasonably drive 100MHz). The high priority FreeRTOS task will be needed to prepare memory pattern buffers in time so you don’t run a buffer dry. By sizing the buffers and perhaps using 2 in a double buffer arrangement you can you can ensure that your buffer task has enough time to complete its task. Using the HW like this will take some effort and reading the datasheet but I think it is the only reasonable path to success.

Thank you Joseph,
I have never used DMA. Assuming constant speed and full step, then the sequence would only be 4 “steps” for an h-bridge at a constant frequency. Would the DMA need to have that same pattern “reloaded” every cycle?
Or are there ways to have the DMA cycle through those patterns in sequence by itself so you set it up once and then run it for as long as you need to without interacting with it?

Thank you

If I recall, the PIC32 DMA has the capability of using a double buffer. You could create a memory large enough to hold a full step cycle and a second memory location with a step cycle. Then it will play both memory blocks back-2-back without a pause. When 1 memory block has completed there will be an interrupt as it transitions to the second block. This will allow you to configure block 1 while block 2 is being played. If you need to accelerate or decelerate the motor you can create different patterns that represent the pulses stretching or shrinking appropriately. At the same time you will play a similar block of memory for the print head. This would simply be a second DMA channel configured in the same manner as the motor channel.

Without knowing the details of your printer, I would try to create an entire print row in memory. Then populate that row with that strip of image and let it run. It seems that the real time aspects are most critical through a row and less critical between rows.

A few years back I used a PIC32 to generate motor step/direction pulses for 6 motors on command from a Raspberry Pi. The Pi would update all 6 motor step counts via SPI every 1ms and the PIC32 used software to generate appropriate pulses as fast as possible. Using tight code on baremetal I was able to reach 250khz step pulses. Doing h-bridge drives at the speeds you are using would be more challenging and will require HW support on nearly any CPU.

I found an example in this thread on the Microchip forum.
https://www.microchip.com/forums/m692652.aspx
A few posts in, there is an example of writing data to an I/O port so it should be a good start for you.

Thank you Joseph for the explanation and link, both very useful!! :slight_smile:

well yes, if you can ensure data integrity outside of FreeRTOS’s scope, imho there is nothing wrong with that, except pragmatic issues such as portability and maintainability. It is one of FreeRTOSs great assets to combine the flexibility to serve such realtime special requirements with structured control flow.

There is nothing inherently wrong with sharing data globally. If is just a single atomically written value written by just one writer, it likely doesn’t even need any form of protection on it.

The key is to be careful about your design and document well your assumptions (like signal atomic writer) and be thinking about possible future changes. (This is the biggest issue with global data, it becomes hard to just see how it is used).

I think I presented a rookie mistake. If applying a intermediary interrupt to communicate with task as you said, how can this improve maintainability for future changes? we may move to another topic, however, I’m indeed confused that application software which involves changing the global vars or ISR handles functions needs to be modified,but how they are different in form of maintainability ?
thank you.

I know about portability and maintainability of FreeRTOS not so much. Can you show us how to impove this skills? are there any guidelines ?

well, your (basically valid) idea by definition is beyond the scope of FreeRTOS, so this is not the right question - the issue I raised is about platform and hardware compatibility. For example, if on your current MCU target, you can rely on atomicity of your 32 bit access functions, your applcation mail fail if you port it to a platform where that isn’t the case. That doesn’t have anything to do with FreeRTOS.