Is it possible to change the Period of a repeating timer (in TimerFcn)?
Intuitively, when programming for Windows, I would handle WM_TIMER messages and use SetTimer to edit the period of a timer, but a similar approach doesn't seem to work in MATLAB, because the timer needs to be restarted in order to change the Period property. This messes up execution, which can be best described as changing the period to near-zero. No errors are produced.
Here's some example code that's used to create a task array: each task item consists of something to do and a delay. The array is basically walked by a timer, which should change its Period based on the current task delay.
function obj = Scheduler(~)
obj.scheduletimer = timer(...
'TimerFcn',@obj.OnTimer,...
'BusyMode','queue',...
'TasksToExecute',length(obj.tasklist),...
'ExecutionMode','fixedRate');
end
function OnTimer(obj,source,event)
obj.Start(); // Executed task, schedule next
end
function Start(obj)
// Stop timer if needed
if(strcmp(obj.scheduletimer.Running,'on'))
stop(obj.scheduletimer);
end;
// Set new period and resume
if(~isempty(obj.tasklist))
obj.scheduletimer.Period = obj.tasklist(1).something;
start(obj.scheduletimer);
end;
end
When I don't mess with the timer in OnTimer, everything obviously works fine, but I'd like to change the Period each iteration.
Edit: I've tried to implement the pingpong solution suggested by Pursuit, but it's still not working. Note that the switching timers idea does work, but periods still don't seem to be applied.
function obj = Scheduler(~)
obj.timer1 = timer(...
'TimerFcn',@obj.OnTimer);
obj.timer2 = timer(...
'TimerFcn',@obj.OnTimer);
end
function OnTimer(obj,source,event)
obj.Start(); // Executed task, schedule next
end
function Start(obj)
if(strcmp(obj.timer1.Running,'on'))
obj.timer2.Period = obj.tasklist{1}{2};
start(obj.timer2);
else
obj.timer1.Period = obj.tasklist{1}{2};
start(obj.timer1);
end;
end
Ugh.
Use two timers, (e.g. timerNamePing and timerNamePong). At the end of the action for each timer setup the next timer to execute once in single shot mode with some delay.
This avoids the need to constantly tear down and create new timers, and avoids the error which occurs when you try and edit a timer which is currently executing.
Here is a working example to demonstrate:
function setupPingPong
timerPing = timer;
timerPong = timer;
timerPing.TimerFcn = @(~,~)pingPongActivity(true, timerPing, timerPong);
timerPing.Name = 'PingTimer';
timerPong.TimerFcn = @(~,~)pingPongActivity(false, timerPing, timerPong);
timerPong.Name = 'PongTimer';
timerPing.StartDelay = 0;
start(timerPing);
function pingPongActivity(isPing, timerPing, timerPong)
if isPing
disp(['PING (' datestr(now,'yyyy-mm-dd HH:MM:SS.FFF') ')'])
else
disp(['PONG (' datestr(now,'yyyy-mm-dd HH:MM:SS.FFF') ')'])
end
delayTime = ceil(rand*10);
display([' delaying ' num2str(delayTime) ' sec.'])
if isPing
nextTimer = timerPong;
else
nextTimer = timerPing;
end
set(nextTimer,'StartDelay', delayTime);
start(nextTimer);
Once this is going, to stop the madness, I use:
t = timerfind; stop(t); delete(t)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With