You have decided: More than 52% voted in Part 1 that the next topic should be Timed Servo Moves. So here we go :-).
This is about how to move the servos over time, instead of moving it to the given position as fast as possible. I’m using a linear approach here: moving the servos linearly over time.
The Principle
Say I want to move the servo position from 0 to 200. But instead of doing this as fast as possible, I want to do this in 1 second. The typical servo PWM period is 20 ms (50 Hz), so I can change or increment the position every 20 ms, such as starting from time t. That would make 200/50=4 steps per 20 ms.
- t+0: position 0
- t+20 ms: position 4
- t+40 ms: position 8
- …
- t+1000 ms: position 200
One approach would be to do this in a loop, simply waiting for 20 ms, then move to the new position. But this would be a waste of microcontroller time. Instead, I’m using a periodic timer interrupts with Triggers.
Trigger Component
The Trigger component implements an infrastructure to call functions at a given time in the future. The functionality is available with the Trigger component available on GitHub. So I’m adding the Trigger component to my ‘Servo’ project I have created in my earlier post:
The Trigger component maintains a list of ‘trigger callbacks’, and if they expire, the callback gets called. The data structure in the Trigger component is simple: a time (in ticks) value, plus the function pointer for the callback:
typedef struct TriggerDesc { word triggerTime; /*!< trigger 'time' in ticks */ void (*callback)(void); /*!< callback function */ } TriggerDesc; static TriggerDesc TriggerList[2]; /*!< Array of triggers */All the triggers are in an array of such Trigger descriptors.
With
AddTrigger()
a trigger gets added to the list:void TRG1_AddTrigger(byte trigger, word incTicks, void (*callback)(void)) { EnterCritical(); TriggerList[trigger].triggerTime = incTicks; TriggerList[trigger].callback = callback; ExitCritical(); }The first argument is a trigger ID which is an index into the array. To keep things simple, no access-out-of-bound check is performed. But as
AddTrigger()
could be called from an interrupt, the code is guarded as critical section.The
AddTick()
method needs to be called with a given frequency (e.g. every 10 ms). It decrements the triggerTime tick counter, and if it is expiring, it calls the call back function pointers inCheckCallbacks()
if they have expired:void TRG1_AddTick(void) { /* This method is usually called from a periodic timer interrupt! */ uint8_t i; for(i=0;i EnterCritical(); if (TriggerList[i].triggerTime!=0) { /* prevent underflow */ TriggerList[i].triggerTime--; } ExitCritical(); } /* for */ while(CheckCallbacks()) { /* while we have callbacks, re-iterate the list as this may have added new triggers at the current time */ } }The frequency of the trigger tick in milliseconds is specified in the component properties:
Timer Interrupt
In case I have an RTOS which already has an event or callback for its tick timer, then I can use the ‘RTOS’ setting, and I do not need a dedicated timer interrupt. As I’m using a bare-metal (no RTOS) project here, I adding a TimerInt component to my project with a new TimerUnit_LDD (I cannot re-use the one for the Servo PWM):
I keep the default of 10 ms trigger tick time, so I configure the timer to call the interrupt handler every 10 ms:
Adding Ticks from the Timer Interrupt
Time to generate code:
Then double-click on the
OnInterrupt()
event:This jumps to the event hook method inside Events.c:
Now I add a call to
AddTick()
to the timer interrupt event. One way is to use drag&drop instead of writing the code:This adds my method call:
Timed Moves in the Servo
For each Servo, I enable the ‘Timed Move’ feature and link it to the Trigger module:
Each of the servos is using its own Trigger ID. So I need to add them to the trigger configuration. For this I need to edit the Trigger Events string list:
The trigger ID’s are named after the component names. As I’m using SERVO1 and SERVO2, I add them to the list, one item per line:
Time to generate the Processor Expert code so my Trigger ID’s get created in TRG1.h. The triggers have the component prefix (TRG1_) added:
I’m going to use these IDs in my source code to move the servos.
Moving the Servo
With the ‘Timed Move’ enabled, the Servo component offers a
MovePos()
method:What it does in
MovePos()
to set up the data structure with information about how many steps it has to do, what is the final (target) PWM and how much it has to change the PWM for each step:/*! * \brief Moves a servo to a given position over a given time. * \param pos Position where to move the servo, must be in servo boundaries (in us) * \param timeMs Time to be used for the move, in milliseconds **/ void SERVO1_MovePos(byte pos, int16_t timeMs) { /* calculate required steps*/ Servo.cnt = timeMs/SERVO1_MOVE_TRIGGER_MS; if(Servo.cnt < 1) { Servo.cnt = 1; /* avoid division by zero */ } Servo.targetPWMus = SERVO1_POS8_TO_PWM(pos); /* calculate increment / decrement per step */ Servo.pwmDeltaPerStep=(int16_t)((Servo.targetPWMus-Servo.currPWMus)/Servo.cnt); DoSteps(); }The real move is done in
DoSteps()
: it changes the PWM, prepares for the next step and sets up the trigger:static void DoSteps(void) { /* Write new duty cycle */ #if SERVO1_INVERTED_PWM Pwm1_SetDutyUS((uint16_t)(SERVO1_PWM_PERIOD_US-(Servo.currPWMus+Servo.pwmDeltaPerStep))); #else Pwm1_SetDutyUS((uint16_t)(Servo.currPWMus+Servo.pwmDeltaPerStep)); #endif /* update current duty cycle value */ Servo.currPWMus += Servo.pwmDeltaPerStep; Servo.cnt--; /* if necessary, call next step by trigger */ if(Servo.cnt>0) { TRG1_AddTrigger(TRG1_SERVO1, SERVO1_MOVE_TRIGGER_TICKS, DoSteps); } }This means that the trigger will call
DoSteps()
through the Trigger periodic timer interrupt every SERVO1_MOVE_TRIGGER_TICKS which is set to 20 ms. As a result the servo will move during the given time as specified byMovePos()
to the new position.Example Code
With this, the following source will move the servo from the current position to the position 100, taking one second (1000 ms):
SERVO1_MovePos(200, 1000);So here is the extended example code: it first moves trough the servo position, and then uses ‘timed’ moves:
void APP_Run(void) { uint16_t pos; /* move each servo from 0 to 255 */ for(pos=0;pos&lt;256;pos++) { SERVO1_SetPos(pos); SERVO2_SetPos(pos); WAIT1_Waitms(50); } /* Use timed move */ for(;;) { SERVO1_MovePos(0, 4000); SERVO2_MovePos(0, 1000); WAIT1_Waitms(8000); /* wait some time */ SERVO1_MovePos(150, 2000); SERVO2_MovePos(150, 500); WAIT1_Waitms(4000); /* wait some time */ } }The code is using
WAIT_Waitms()
between the moves to give the servos enough time to move.💡 The latest version of the Servo component sources on GitHub features a method IsMoving() which returns TRUE if the trigger is still working on moving the servo.
The project discussed here is available on GitHub here.
List of Tutorials
- Tutorial: Arduino Motor/Stepper/Servo Shield – Part 1: Servos
- Tutorial: Arduino Motor/Stepper/Servo Shield – Part 2: Timed Servo Moves
- Tutorial: Arduino Motor/Stepper/Servo Shield – Part 3: 74HCT595 Shift Register
Happy Moving 🙂
What’s Next?
Now it is up to you to vote what should be the next topic 🙂 :
Erich, great post!, i can’t wait to test servos in my board. I must note there is an error in the webpage. The text format is a mess. Your website has been a great help for me. Thank you!
LikeLike
Hi Juan,
Thanks for pointin to the format mess. WordPress does this sometimes to me if I copy-paste source text. I need to be more careful the next time. It should look ok now.
Thanks!
LikeLike
Pingback: Tutorial: Arduino Motor/Stepper/Servo Shield – Part 1: Servos | MCU on Eclipse
Pingback: Tutorial: Arduino Motor/Stepper/Servo Shield – Part 3: 74HCT595 Shift Register | MCU on Eclipse
This tutorial is great for a periodic interrupt that has a constant rate, but my project requires a programmable interrupt. For my project I read some external interfaces, and those determine how long I need to assert an output. My though was to use the TimerInt component, and then configure the different modes for the different timing ranges. Then I can pole in inputs, set the mode, set the period, enable the component (start the timer), enable the event and start the output. In the interrupt event I could turn off my output and disable the component. (Hope that wasn’t confusing, I’m open to suggestions.)
I am of course able to add the component to the project, but once I setup the “Interrupt period” to be a list of values, configuring each of the 15 modes to be one of the ranges. Once I click okay, I get an error. “ERROR: Inherited component does not support this feature: Runtime setting type list.”
Do you have a tutorial that explains how to configure the TimerInt component and the TimerUnit_LDD referenced component for dynamic interrupt periods?
Thanks in advance!
LikeLike
Yes, Freescale changed the API in the Processor Expert components, and not all the functions and features are available any more from the TimerInt component on Kinetis :-(. You can either use directly the Timer _LDD component, but not sure if this will work. The other thing what I did in such cases is to use the PDD macros (see https://mcuoneclipse.com/2013/05/11/low-level-coding-with-pdd-physical-device-driver/) which allow direct modification of the peripherals.
I hope this helps,
Erich
LikeLike