I'm working on a Portenta H7 with a mid-carrier to improve motor movement smoothness. I'm using the AccelStepper library to generate pulses in a continuous loop until distanceToGo reaches zero. Meanwhile, I need to check for stop commands from a Modbus table using ModbusTCPClient from the ArduinoModbus library.
Currently, I’m handling both stepper control and Modbus checking in a single thread on the M7 core. However, when the Modbus table is read (which takes around 1ms), the motor trembles due to a slight halt in pulse generation. To eliminate these interruptions, I’m considering offloading Modbus reading to the M4 core while keeping motor control uninterrupted on M7.
Here’s my approach in theory. M4 continuously reads the Modbus table and updates a shared memory space (SRAMx) with the latest command variable. M7 reads from shared memory in real-time, ensuring non-blocking motor control. An RPC call from M4 to M7 could be used to notify when a new command arrives, avoiding unnecessary polling on M7 which I refer to this articles Portenta H7 dual core shared memory
Is this a practical way to prevent any delay when motor is running and improve real-time performance?
Are there any better alternatives to achieve non-blocking stepper control while maintaining real-time Modbus updates?
Hi @thuang66, I'm doing something similar with my GIGA but I'm offloading USB flashdrive logging to the M4 rather than offloading modbus. It works very well using SRAM3 to exchange data between the cores. A few notes from my experiences:
Avoid using RPC. I found it to be too much of a performance overhead. Polling the shared memory is much more efficient in my case.
You'll need to turn off caching of SRAM3 in the M7
If you need two-way exchanges between the cores then a strategy to avoid conflict is advisable
You can use OTA to update the M4 just as easily as the M7
However, first I would look for a way of controlling the motor that is less susceptible to code that blocks (as there's a lot of it). I don't have experience with stepper motors but I do have 29 servos in my system so I've offloaded the pulse generation to a dedicated servo controller.
There are stepper libraries such as FastAccelStepper and MobaTools which use a timer interrupt, so that they can run in the background without polling. However, they do not appear to support Portenta.
I wonder if it is possible to set up a timer interrupt yourself and have that call AccelStepper.
Otherwise, a dual core approach is viable. I did wonder about overheads with the RPC. I think you can't avoid polling, but the overhead is minimal.
In the absence of RPC, you can set up a shared area to do IPC (inter process communication), but you need to take some care over the design and implementation if you have multiple values to transfer. For updating a single 32 bit word, the access should be atomic and apart from disabling caching, and declaring as volatile, nothing else is needed.
For multiple values, or to implement a queue, you could use a ring buffer with an "in count" and "out count". The sender increments the "in count", and the receiver increments the "out count". This avoids contention, but only provides transfer in a single direction.
If you need bidirectional transfer, you could have one buffer for commands, and a separate buffer for responses.
I used this scheme for a disk driver running over dual cores and it worked really well.