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:
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
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.