re-entrant motor control

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
Post Reply
anthonykoeslag
Posts: 1
Joined: 17 Aug 2011, 07:49

re-entrant motor control

Post by anthonykoeslag »

Hi all

I'm using NXC, and I'm curious about what causes the following behaviour, and wondering how to specify re-entrant functions if thats possible.

If I user the following code:

RotateMotor(OUT_A, 50, 360);
RotateMotor(OUT_C, 50, 360);

First motor A moves and only when that’s done motor C moves.

If I use:

PosRegAddAngle(OUT_A, 360);
PosRegAddAngle(OUT_C, 360);

I get both motors running in parallel.

From this I figure that its possible to specify that some functions are re-entrant and others not.
How is this done? Or have I come to the wrong conclusion and there is another reason for these functions to behave differently?

Thanks
Anthony
linusa
Posts: 228
Joined: 16 Oct 2010, 11:44
Location: Aachen, Germany
Contact:

Re: re-entrant motor control

Post by linusa »

I think it's more of a "blocking" vs "non-blocking" thing, hm?

Anyway, the RotateMotor*() commands are all blocking until the motors have reached their target. The other calls are pretty much all non-blocking.

I don't know about re-entrant in general in NXC. Many functions are actually macros behind the back, so this should work. On the other hand, with motor commands, you're always setting IO map parameters/registers, so you're basically working on global data. This leads to the fact that you sometimes need a Wait(1) between consecutive motor commands for the same motor, to "commit" the settings and update this global motor state.

So, having said this, with multi-tasking in NXC, it's good practice to always shield motor access (at least to the same motors) with mutexes!


In some compiler version a long time ago RotateMotor() was in fact NOT re-entrant, and calling it from multiple tasks with different motors caused problems I couldn't explain. However, I think that's been fixed.
RWTH - Mindstorms NXT Toolbox for MATLAB
state of the art in nxt remote control programming
http://www.mindstorms.rwth-aachen.de
MotorControl now also in Python, .net, and Mathematica
spillerrec
Posts: 358
Joined: 01 Oct 2010, 06:37
Location: Denmark
Contact:

Re: re-entrant motor control

Post by spillerrec »

Most functions on the NXT internally takes a structure containing the parameters. So most macro functions copy the values into a globally shared struct and then call the function. I suspect the issues were that this struct was changed by two threads simultaneously, causing unexpected results. (Even if it was different motors it would be the same struct.)
It looks like these macros are mutex protected now (but optimized out if unnecessary).
I'm not too familiar with the motor functions though as I haven't done any real robotics programming for a long time now...


If you need to use a function across different threads you need to be aware of that you may not call the function while it is running in a different thread. (So no, re-entrant functions are not possible.) If there is a chance of this happening, you must protect the function call with a mutex. This can be done automatically by specifying a function as safecall.
My blog: http://spillerrec.dk/category/lego/
RICcreator, an alternative to nxtRICeditV2: http://riccreator.sourceforge.net/
linusa
Posts: 228
Joined: 16 Oct 2010, 11:44
Location: Aachen, Germany
Contact:

Re: re-entrant motor control

Post by linusa »

I just also remembered that any "usual normal" NXC function you define yourself is NOT reentrant. When you have

Code: Select all

function func(int x) {

  doSomethingA(x);
  doSomethingB(x);
  doSomethingC(x);
}
And you call func(myVar1) from one task and at (roughly) the same time func(myVar2) from another task, this can happen:
Task1 "jumps" into func, now param x is set to myVar1. Let's assume doSomethingA() has just completed, and execution is inside doSomethingB(). Now task2 calls func(). What happens is that x gets overwritten. Now x is myVar2, and doSomethingA(myVar1) is called. At the same time, task1 may finish doSomethingB(x) and goes to doSomethingC(x). But unlike expected, x is not myVar1 anymore, but myVar2!!!

So this messes up the functions variables. I think the definition of reentrant is: If you supply different variables to a reentrant function, it will work. In this case, with func() as I described it, that's not the case.

Better make manual copies of func() for each task, like func1() and func2().

If you don't want to manually copy-paste the functions (bad style), you can use macros and preprocessor tricks to include that function multiple times and let it be renamed "automatically". I did this with my motor functions in MotorControl, see source here: http://www.mindstorms.rwth-aachen.de/tr ... torControl . The file Controller.nxc includes ControllerCore.nxc 4 times, and the functions in there have this form:

Code: Select all

inline bool _ADD_MOTORNAME(RunMotor) (...

The macro is

Code: Select all

#define _ADD_MOTORNAME(funcname) funcname##RUNMOTOR_CURRENTPORTNAME
So the trick to get different versions of that functions is

Code: Select all

#define RUNMOTOR_CURRENTPORTNAME A
#include "ControllerCore.nxc"

#undef RUNMOTOR_CURRENTPORTNAME
#define RUNMOTOR_CURRENTPORTNAME B
#include "ControllerCore.nxc"

#undef RUNMOTOR_CURRENTPORTNAME
#define RUNMOTOR_CURRENTPORTNAME C
#include "ControllerCore.nxc"
	
#undef RUNMOTOR_CURRENTPORTNAME
If you do this, you can use the functions RunMotorA(), RunMotorB() and RunMotorC() from your main program without any side effects. I did exactly that to bypass this "not re-entrant" stuff...
RWTH - Mindstorms NXT Toolbox for MATLAB
state of the art in nxt remote control programming
http://www.mindstorms.rwth-aachen.de
MotorControl now also in Python, .net, and Mathematica
Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests