if i draw something on screen now it is displayed as fast as possible, there is no limit besides the hardware.
What if i want do draw something every second ?(lets say a digital clock)
Can i use the RTC ? or do i need some kind of function ?
Thereās also a way to make it so multiple events can share the same timer, but it involves an extra function.
By offloading the work into an update function thatās called at the start of every loop and then storing the result internally, the timer can be used to synchronise multiple actions.
(Anything that needs to use the timer will need a reference or a pointer to the timer though, copying/passing by value wonāt work.)
e.g.
#include "Pokitto.h"
#include "Timer.h"
Pokitto::Core pokitto;
int main ()
{
Timer timer1 = Timer(500);
Timer timer2 = Timer(1000);
pokitto.begin();
while (pokitto.isRunning())
{
if (pokitto.update())
{
timer1.update();
timer2.update();
pokitto.display.print("Hello World!");
if(timer1.hasDurationElapsed())
pokitto.display.print("Half second");
if(timer2.hasDurationElapsed())
pokitto.display.print("Full second");
}
}
}
Hereās the modified code samples.
(Bear in mind I havenāt tested either of these, or the code I posted before. If they donāt compile or donāt work, let me know and Iāll look into it. Time is a hard thing to get right.)
The three-point fix:
// One timer per event
class Timer
{
public:
// Not completely necessary, but it future-proofs
// the code in case `getTime` is changed
// to return a different (i.e. larger) type
using TimeType = decltype(Pokitto::Core::getTime());
using DurationType = decltype(TimeType(0) - TimeType(0));
private:
bool firstRun = true;
bool hasElapsed = false;
TimeType previousTime = 0;
DurationType targetDuration = 0;
public:
Timer(DurationType targetDuration)
: targetDuration(targetDuration)
{
}
DurationType getTargetDuration(void) const
{
return this->targetDuration;
}
void reset(void)
{
this->firstRun = true;
}
void update(void)
{
if(this->firstRun)
{
this->previousTime = Pokitto::Core::getTime();
this->firstRun = false;
this->hasElapsed = false;
}
const auto now = Pokitto::Core::getTime();
const auto duration = now - this->previousTime;
if (duration >= this->targetDuration)
{
this->previousTime = Pokitto::Core::getTime();
this->hasElapsed = true;
}
this->hasElapsed = false;
}
bool hasDurationElapsed(void) const
{
return this->hasElapsed;
}
};
The four-point fix.
// One timer per event
class Timer
{
public:
// Not completely necessary, but it future-proofs
// the code in case `getTime` is changed
// to return a different (i.e. larger) type
using TimeType = decltype(Pokitto::Core::getTime());
using DurationType = decltype(TimeType(0) - TimeType(0));
private:
bool firstRun = true;
bool hasElapsed = false;
TimeType previousTime = 0;
DurationType targetDuration = 0;
public:
Timer(DurationType targetDuration)
: targetDuration(targetDuration)
{
}
DurationType getTargetDuration(void) const
{
return this->targetDuration;
}
void reset(void)
{
this->firstRun = true;
}
void update(void)
{
if(this->firstRun)
{
this->previousTime = Pokitto::Core::getTime();
this->firstRun = false;
this->hasElapsed = false;
}
const auto now = Pokitto::Core::getTime();
if(now < this->previousTime)
{
const auto timeUntilOverflow = TimeType(~0) - this->previousTime;
if((now + timeUntilOverflow) > targetDuration)
{
this->previousTime = now;
this->hasElapsed = true;
}
}
else
{
const auto duration = now - this->previousTime;
if (duration >= this->targetDuration)
{
this->previousTime = Pokitto::Core::getTime();
this->hasElapsed = true;
}
}
this->hasElapsed = false;
}
bool hasDurationElapsed(void) const
{
return this->hasElapsed;
}
};
Itās also possible to get rid of the firstRun logic by moving it into a begin function, but it would make reset behave differently and begin would have to be called at the start as well.
There are other ways to handle it as well, like a delta time based timer, but thatās going to start getting more complicated than providing a single class. (If start discussing delta timing my comments will end up being 2-4 times larger.)
Time can be a pretty hard thing to deal with in code.
Though thereās a lot of much scarier out there (at least some of which was written by me).
Pretty much.
If youāre using the second version then just call update near the start of your if(pokitto.update()) block.
Iām more confident in the 3-point fix than the 4-point fix but the likelihood of the timer overflowing is pretty low (youād need to have the Pokitto running for about 49.71 days before the timer rolls over) so you probably wonāt need the 4-point version.
Try my example code first to make sure it works before adding it to anything complicated.
Or maybe something like:
#include "Pokitto.h"
#include "Timer.h"
Pokitto::Core pokitto;
int main ()
{
Timer timer1 = Timer(500);
Timer timer2 = Timer(1000);
int counter1 =0;
int counter2 = 0;
pokitto.begin();
while (pokitto.isRunning())
{
if (pokitto.update())
{
timer1.update();
timer2.update();
pokitto.display.print("Hello World!");
pokitto.display.print(counter1);
pokitto.display.print(counter2);
if(timer1.hasDurationElapsed())
++counter1;
if(timer2.hasDurationElapsed())
++counter2;
}
}
}
Well, it did not work. But i do understand what your intention was and the Gettime does give me a steady number to work with. So i am working from that point and see where it gets me.
I am using the mbed online compiler, i added a .h file for the timer and placed the #pragma on top.
Then i tried your example code to see if that would compile(inside the main.cpp). This caused a buch of errors, i couldnt say what all those errors meant as i dont fully understand the code.
i got :
Error: Data member initializer is not allowed in āTimer.hā, Line: 11, Col: 20
Error: Identifier ā_runningā is undefined in āPokittoLib/mbed-pokitto/common/Timer.cppā, Line: 31, Col: 11
and a bunch more like that.
But it could easily be that i build the structure incorrect, i am still new to C++ (and the compiler)
You did gave me a idea about setting the pace using the gettime() so i am working that out for myself, just to learn and understand the code.
I just tried compiling on Embitz and ran into the same issue, so it looks like thereās probably a using namespace mbed; somewhere in the library thatās upsetting things, among other possible issues.
The easiest solution to the name collision is to rename the file and the class. Maybe something like SimpleTimer.h and class SimpleTimer.
When testing I realised I missed an else anyway so Iāll add that back in.
If the ādata member initialiser is not allowedā error comes up again, I think I might know whatās wrong. (Especially if youāre also getting an error about using or decltype, in which case Iāll definitely know whatās wrong and Iāll have to rewrite the code again.)
If itās the problem I think it is, itās something that wouldnāt be an issue with Embitz, just the online compiler.
(In case youāre curious, I think the online compiler doesnāt support C++11, and Iām using some C++11 techniques in the code.)
I didnāt notice the mbed::Timer class already included with the Pokitto lib. That might also be suitable, but it looks like itās a bit heavier than the one I wrote, and has more features.
3-point fix version 2 (actually tested - it compiles and works)
// One timer per event
class SimpleTimer
{
public:
// Not completely necessary, but it future-proofs
// the code in case `getTime` is changed
// to return a different (i.e. larger) type
using TimeType = decltype(Pokitto::Core::getTime());
using DurationType = decltype(TimeType(0) - TimeType(0));
private:
bool firstRun = true;
bool hasElapsed = false;
TimeType previousTime = 0;
DurationType targetDuration = 0;
public:
SimpleTimer(DurationType targetDuration)
: targetDuration(targetDuration)
{
}
DurationType getTargetDuration(void) const
{
return this->targetDuration;
}
void reset(void)
{
this->firstRun = true;
}
void update(void)
{
if(this->firstRun)
{
this->previousTime = Pokitto::Core::getTime();
this->firstRun = false;
this->hasElapsed = false;
}
const auto now = Pokitto::Core::getTime();
const auto duration = now - this->previousTime;
if (duration >= this->targetDuration)
{
this->previousTime = Pokitto::Core::getTime();
this->hasElapsed = true;
}
else
{
this->hasElapsed = false;
}
}
bool hasDurationElapsed(void) const
{
return this->hasElapsed;
}
};
Sample code:
#include "Pokitto.h"
#include "SimpleTimer.h"
Pokitto::Core pokitto;
int main ()
{
SimpleTimer timer1 = SimpleTimer(500);
SimpleTimer timer2 = SimpleTimer(1000);
int counter1 = 0;
int counter2 = 0;
pokitto.begin();
while (pokitto.isRunning())
{
if (pokitto.update())
{
timer1.update();
timer2.update();
pokitto.display.println("Hello World!");
pokitto.display.println(counter1);
pokitto.display.println(counter2);
if(timer1.hasDurationElapsed())
++counter1;
if(timer2.hasDurationElapsed())
++counter2;
}
}
}