When Off Doesn't Stop the Motor
When Off Doesn't Stop the Motor
Is this an NXC bug or have I completely missed the boat? A couple of days ago I ran into a problem that caused me some grief for an hour before I isolated the problem and devised a workaround. The problem can be illustrated by the following NXC code fragment:
100 OnFwd(OUT_A,30);
101 Wait(2000);
102 Off(OUT_A);
103 ResetRotationCount(OUT_A);
My expectation was that the motor should stop at line 102, but it just kept on going indefinitely.
My workaround was to place a Wait(1); statement between lines 102 and 103. In this case the motor stopped but placing an assignment statement (i=6) in the same position had no effect. Normally my Off statements work just fine. This is the first time it has been followed with a ResetRotationCount statement.
I am running BricxCC version 3.3 build 3.3.8.7 (I think the compiler version is 1.2.1.r2) on a Dell Studio XPS laptop with Core i7 running Windows 7 Professional 64 bit.
Noel.
100 OnFwd(OUT_A,30);
101 Wait(2000);
102 Off(OUT_A);
103 ResetRotationCount(OUT_A);
My expectation was that the motor should stop at line 102, but it just kept on going indefinitely.
My workaround was to place a Wait(1); statement between lines 102 and 103. In this case the motor stopped but placing an assignment statement (i=6) in the same position had no effect. Normally my Off statements work just fine. This is the first time it has been followed with a ResetRotationCount statement.
I am running BricxCC version 3.3 build 3.3.8.7 (I think the compiler version is 1.2.1.r2) on a Dell Studio XPS laptop with Core i7 running Windows 7 Professional 64 bit.
Noel.
Re: When Off Doesn't Stop the Motor
you are right, I tested it (3.3.8.8.20101010).
the motor stops only after 2 sec if the Reset... statement has been outcommented, or it runs infinitely:
BTW: try to install a newer bricx version anyway!
the motor stops only after 2 sec if the Reset... statement has been outcommented, or it runs infinitely:
Code: Select all
task main(){
OnFwd(OUT_A,30);
Wait(2000);
Off(OUT_A);
//ResetRotationCount(OUT_A);
while (true);
}
Re: When Off Doesn't Stop the Motor
This is an issue with motor commands in general and how the NXT firmware VM executes the code in your program. The post Morton recently added to the Software forum about the way the firmware handles multitasking is a good foundation for explaining why what you are seeing happens.
Each motor command uses the setout opcode to set various fields in the Output module's IOMap structure. One of the fields is the UpdateFlags field. If you want to change the power setting of a motor you not only have to change the Power field to the new value but you also have to set the UF_UPDATE_SPEED bit in the UpdateFlag field so that when the Output module's Ctrl function is executed by the NXT firmware it will see that someone has asked it to update the speed and given it a new value to use. When you call Off(OUT_A) it sets these values in UpdateFlags:
UF_UPDATE_TACHO_LIMIT+UF_UPDATE_MODE+UF_UPDATE_SPEED+UF_UPDATE_PID_VALUES
It also sets Power to 0, OutputMode to OUT_MODE_MOTORON+OUT_MODE_BRAKE, RegMode to OUT_REGMODE_IDLE, RunState to OUT_RUNSTATE_RUNNING, TurnRation to 0, TachoLimit to 0, RegPValue to PID_3, RegIValue to PID_1, and RegDValue to PID_1.
If after setting these fields in the Output module's IOMap you immediately gave the Output module a chance to execute its Ctrl function then it would correctly change the motor's state accordingly. But the VM doesn't give other modules in the firmware a chance to execute their Ctrl function (in a round robin fashion) until the program running in the VM has either used up its 1ms of execution time or every thread in the running program has executed 20 opcodes (the details are a bit more complicated but they are described somewhat in the post Morton created). So while the VM (which is running under the Command module's Ctrl function) does its thing the Output module is doing nothing and it won't start doing something until the firmware switches from running code in your program under the Command module's Ctrl function to running the other firmware module Ctrl functions. If you decide to change the fields in the Output module IOMap structure a second time before the 1ms has elapsed you are free to do so, though there is no guarantee that two back to back opcodes executing in your program will both occur before the firmware switches to executing the Output module's Ctrl function.
All that ResetRotationCount does is call setout with just the UpdateFlags field and the value UF_UPDATE_RESET_ROTATION_COUNT. If you do this:
then it is indeterminate whether both of these will execute in a single pass through the VM in the Command module or whether one will execute then the Output module's Ctrl function will execute and then the second will execute. If they both execute on a single VM pass then you have essentially eliminated the affect of the first call since the second one does not set UpdateFlags to include the TACHO_LIMIT bits, the MODE bits, the SPEED bits or the PID bits.
There are basically 4 options to fix this problem
1. The ResetRotationCount function could read the current UpdateFlags value using getout and then OR in the UF_UPDATE_RESET_ROTATION_COUNT value. That would make it take twice (or three times) as long to execute but it would not overwrite existing UpdateFlags waiting to be processed by the Output module. This may be the best option if changes are made to any API functions.
2. The Off function or the ResetRotationCount function could include a wait 1 or wait 0 opcode either at the end (Off) or the beginning (ResetRotationCount). This would force the VM to never execute these two operations back to back before the Output module has run its Ctrl function. It would also introduce a wait that may not typically be needed if you never put these functions next to each other. The compiler would be deciding when your code should wait even if you write your code in a way that never requires the extra waits. I don't favor this option at all.
3. Instead of calling ResetRotationCount after calling Off you can call OffEx and pass in RESET_ROTATION_COUNT as a second parameter. It will add that flag to the existing bits it sets in the UpdateFlags field in a single call to setout. Generally, I think this is the best option.
4. Call Wait(MS_1) or Yield() between back to back motor control function calls or if your program behaves unexpectedly wrt motor control.
There were discussions on the old forums about calling ResetRotationCount and then immediately reading the rotation count using the MotorRotationCount API function where this same kind of problem occurs. In this case you would either need the ResetRotationCount API function to wait after it executes setout so that the Output module would actually get a chance to reset the counter before you read it or you would have to wait before you call getout (in MotorRotationCount) to read the counter. I definitely don't want to build in waits before and after all these API functions since they are not always needed.
John Hansen
Each motor command uses the setout opcode to set various fields in the Output module's IOMap structure. One of the fields is the UpdateFlags field. If you want to change the power setting of a motor you not only have to change the Power field to the new value but you also have to set the UF_UPDATE_SPEED bit in the UpdateFlag field so that when the Output module's Ctrl function is executed by the NXT firmware it will see that someone has asked it to update the speed and given it a new value to use. When you call Off(OUT_A) it sets these values in UpdateFlags:
UF_UPDATE_TACHO_LIMIT+UF_UPDATE_MODE+UF_UPDATE_SPEED+UF_UPDATE_PID_VALUES
It also sets Power to 0, OutputMode to OUT_MODE_MOTORON+OUT_MODE_BRAKE, RegMode to OUT_REGMODE_IDLE, RunState to OUT_RUNSTATE_RUNNING, TurnRation to 0, TachoLimit to 0, RegPValue to PID_3, RegIValue to PID_1, and RegDValue to PID_1.
If after setting these fields in the Output module's IOMap you immediately gave the Output module a chance to execute its Ctrl function then it would correctly change the motor's state accordingly. But the VM doesn't give other modules in the firmware a chance to execute their Ctrl function (in a round robin fashion) until the program running in the VM has either used up its 1ms of execution time or every thread in the running program has executed 20 opcodes (the details are a bit more complicated but they are described somewhat in the post Morton created). So while the VM (which is running under the Command module's Ctrl function) does its thing the Output module is doing nothing and it won't start doing something until the firmware switches from running code in your program under the Command module's Ctrl function to running the other firmware module Ctrl functions. If you decide to change the fields in the Output module IOMap structure a second time before the 1ms has elapsed you are free to do so, though there is no guarantee that two back to back opcodes executing in your program will both occur before the firmware switches to executing the Output module's Ctrl function.
All that ResetRotationCount does is call setout with just the UpdateFlags field and the value UF_UPDATE_RESET_ROTATION_COUNT. If you do this:
Code: Select all
setout OUT_A, Power, 0, OutputMode, OUT_MODE_MOTORON+OUT_MODE_BRAKE, RegMode, OUT_REGMODE_IDLE, RunState, OUT_RUNSTATE_RUNNING, TurnRatio, 0, TachoLimit, 0, RegPValue, PID_3, RegIValue, PID_1, RegDValue, PID_1, UpdateFlags, UF_UPDATE_TACHO_LIMIT+UF_UPDATE_MODE+UF_UPDATE_SPEED+UF_UPDATE_PID_VALUES
setout OUT_A, UpdateFlags, UF_UPDATE_RESET_ROTATION_COUNT
There are basically 4 options to fix this problem
1. The ResetRotationCount function could read the current UpdateFlags value using getout and then OR in the UF_UPDATE_RESET_ROTATION_COUNT value. That would make it take twice (or three times) as long to execute but it would not overwrite existing UpdateFlags waiting to be processed by the Output module. This may be the best option if changes are made to any API functions.
2. The Off function or the ResetRotationCount function could include a wait 1 or wait 0 opcode either at the end (Off) or the beginning (ResetRotationCount). This would force the VM to never execute these two operations back to back before the Output module has run its Ctrl function. It would also introduce a wait that may not typically be needed if you never put these functions next to each other. The compiler would be deciding when your code should wait even if you write your code in a way that never requires the extra waits. I don't favor this option at all.
3. Instead of calling ResetRotationCount after calling Off you can call OffEx and pass in RESET_ROTATION_COUNT as a second parameter. It will add that flag to the existing bits it sets in the UpdateFlags field in a single call to setout. Generally, I think this is the best option.
4. Call Wait(MS_1) or Yield() between back to back motor control function calls or if your program behaves unexpectedly wrt motor control.
There were discussions on the old forums about calling ResetRotationCount and then immediately reading the rotation count using the MotorRotationCount API function where this same kind of problem occurs. In this case you would either need the ResetRotationCount API function to wait after it executes setout so that the Output module would actually get a chance to reset the counter before you read it or you would have to wait before you call getout (in MotorRotationCount) to read the counter. I definitely don't want to build in waits before and after all these API functions since they are not always needed.
John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
http://bricxcc.sourceforge.net/
Re: When Off Doesn't Stop the Motor
this works:2. The Off function or the ResetRotationCount function could include a wait 1 or wait 0 opcode either at the end (Off) or the beginning (ResetRotationCount). This would force the VM to never execute these two operations back to back before the Output module has run its Ctrl function. It would also introduce a wait that may not typically be needed if you never put these functions next to each other. The compiler would be deciding when your code should wait even if you write your code in a way that never requires the extra waits. I don't favor this option at all.
Code: Select all
task main(){
OnFwd(OUT_A,30);
Wait(2000);
Off(OUT_A);
Wait(1);
ResetRotationCount(OUT_A);
while (true);
}
this doesn't:
There were discussions on the old forums about calling ResetRotationCount and then immediately reading the rotation count using the MotorRotationCount API function where this same kind of problem occurs. In this case you would either need the ResetRotationCount API function to wait after it executes setout so that the Output module would actually get a chance to reset the counter before you read it or you would have to wait before you call getout (in MotorRotationCount) to read the counter. I definitely don't want to build in waits before and after all these API functions since they are not always needed.
Code: Select all
task main(){
OnFwd(OUT_A,30);
Wait(2000);
Off(OUT_A);
ResetRotationCount(OUT_A);
long dummy=MotorRotationCount(OUT_A);
while (true);
}
can you fix this problem that is works "automatically correct"?
Re: When Off Doesn't Stop the Motor
I'm not inclined to, as I explained above. If you want to reset the rotation count after Off then use OffEx and pass in the appropriate reset flag. If you want to immediately read the rotation count after resetting it then call Yield() or Wait(MS_1) before you read the rotation count. All motor commands would need to be changed to wait after or wait before and those waits are not needed 100% of the time so they slow down your code in many cases for no reason at all. I'd rather a user decide when to wait than have the compiler decide - unless it is absolutely needed and is more of a configuration/setup (like SetSensor*) function.doc-helmut wrote: can you fix this problem that is works "automatically correct"?
What's really nice is you can write a FordOff API function and FordResetRotationCount function and FordMotorRotationCount function which adds a wait either before or after the underlying API call. Then use your functions instead of the NXC API functions.
John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
http://bricxcc.sourceforge.net/
Re: When Off Doesn't Stop the Motor
I foresee a 3rd Party NXC Driver Suite in Ford's near future! Ford, if you'll write, we'd be happy to host it on the SourfeForge project page. While you're at it, you can put the RS485 remote control API library on there as well.afanofosc wrote:What's really nice is you can write a FordOff API function and FordResetRotationCount function and FordMotorRotationCount function which adds a wait either before or after the underlying API call. Then use your functions instead of the NXC API functions.
Thanks,
Xander
| My Blog: I'd Rather Be Building Robots (http://botbench.com)
| RobotC 3rd Party Driver Suite: (http://rdpartyrobotcdr.sourceforge.net)
| Some people, when confronted with a problem, think, "I know, I'll use threads,"
| and then two they hav erpoblesms. (@nedbat)
| RobotC 3rd Party Driver Suite: (http://rdpartyrobotcdr.sourceforge.net)
| Some people, when confronted with a problem, think, "I know, I'll use threads,"
| and then two they hav erpoblesms. (@nedbat)
Re: When Off Doesn't Stop the Motor
As I said before, I'm no good in API or low level programming, and I do not know anything about NBC at all. If I was I did it for sure, and I wouldn't have asked if someone else could do it.
I say this without any irony.
I can only point out where the problems or bugs are - in order that someone else is able to care about it and many others who may have similar problems may benefit.
@Xander:
I'm not sure what you like me to do - what sf project are you talking about?
But if you explain it again and I can help, I surely will try to help.
I say this without any irony.
I can only point out where the problems or bugs are - in order that someone else is able to care about it and many others who may have similar problems may benefit.
@Xander:
I'm not sure what you like me to do - what sf project are you talking about?
But if you explain it again and I can help, I surely will try to help.
Re: When Off Doesn't Stop the Motor
I am not entirely opposed to the idea of making API level changes to ensure that two separate motor commands do not "interfere" with each other, as it were. But it is easy to use OffEx rather than Off if you want to immediate reset the rotation counter after stopping the motors and it is easy to Yield() or Wait(MS_1) before reading rotation counts after resetting them. Changing the API functions would impact everyone who uses NXC while making the changes in your own program only impacts 1 person. People who like the way it works now, for whatever reason, might be annoyed if I changed things out from under them. Maybe that extra wait breaks the behavior of their robot.
I would prefer to focus my limited time and efforts on what seem to be more important time consuming tasks.
John Hansen
I would prefer to focus my limited time and efforts on what seem to be more important time consuming tasks.
John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
http://bricxcc.sourceforge.net/
Re: When Off Doesn't Stop the Motor
yes, I certainly agree.
If one knows that a Wait(1) will help, he may use it.
I thought a patch in the API to erase the bug might be easier than it probably really is.
If one knows that a Wait(1) will help, he may use it.
I thought a patch in the API to erase the bug might be easier than it probably really is.
Re: When Off Doesn't Stop the Motor
Well, strictly speaking "Mindboards" is a Source Forge project with a subversion repository and all the other bells and whistles that come with a project. So we could host software on itdoc-helmut wrote:I'm not sure what you like me to do - what sf project are you talking about? But if you explain it again and I can help, I surely will try to help.
I don't think you need to be able to write NBC to write libraries for NXC. I've written a few and I've never written a single line of NBC code (I wouldn't even know how). Some of the features you want added to the firmware are easily implemented in NXC with only a very minimal amount of overhead. An example of that would be the Off() call. Most of the stuff I've written were in response to unmet needs in my programming environments. Instead of asking (and waiting for) the developers to add the stuff, I did it myself and released the code for everyone else to use. You could easily do the same. Write the whole thing in German if English is too tricky for you, so you can focus on the code, rather than the comments. I am sure others will be happy to help you translate it to English.
- Xander
| My Blog: I'd Rather Be Building Robots (http://botbench.com)
| RobotC 3rd Party Driver Suite: (http://rdpartyrobotcdr.sourceforge.net)
| Some people, when confronted with a problem, think, "I know, I'll use threads,"
| and then two they hav erpoblesms. (@nedbat)
| RobotC 3rd Party Driver Suite: (http://rdpartyrobotcdr.sourceforge.net)
| Some people, when confronted with a problem, think, "I know, I'll use threads,"
| and then two they hav erpoblesms. (@nedbat)
Who is online
Users browsing this forum: No registered users and 1 guest