Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor sleep and loop #232

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 15 additions & 21 deletions src/Scout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,6 @@ void PinoccioScout::setup(const char *sketchName, const char *sketchRevision, in
void PinoccioScout::loop() {
now = SleepHandler::uptime().seconds;

bool canSleep = true;
// TODO: Let other loop functions return some "cansleep" status as well

PinoccioClass::loop();

// every 5th second blink network status
Expand Down Expand Up @@ -188,15 +185,12 @@ void PinoccioScout::loop() {
Led.setBlueValue(Led.getBlueValue());
}

if (sleepPending) {
canSleep = canSleep && !NWK_Busy();
// TODO: suspend more stuff? Wait for UART byte completion?
// TODO: Let other loop functions return some "cansleep" status as well
bool canSleep = !NWK_Busy();

// if remaining <= 0, we won't actually sleep anymore, but still
// call doSleep to run the callback and clean up
if (SleepHandler::scheduledTicksLeft() == 0)
doSleep(true);
else if (canSleep)
doSleep(false);
if (sleepPending && canSleep) {
doSleep();
}
}

Expand Down Expand Up @@ -656,26 +650,27 @@ void PinoccioScout::scheduleSleep(uint32_t ms, const char *func) {
sleepMs = ms;
}

void PinoccioScout::doSleep(bool pastEnd) {
void PinoccioScout::doSleep() {
// Copy the pointer, so the post command can set a new sleep
// timeout again.
char *func = postSleepFunction;
postSleepFunction = NULL;
sleepPending = false;

if (!pastEnd) {
NWK_SleepReq();
NWK_SleepReq();

uint32_t left = SleepHandler::doSleep();

// TODO: suspend more stuff? Wait for UART byte completion?
// we've now woken up
NWK_WakeupReq();

SleepHandler::doSleep(true);
NWK_WakeupReq();
//if there no ticks left, stop sleeping
if (!left) {
sleepPending = false;
}

// TODO: Allow ^C to stop running callbacks like this one
if (func) {
StringBuffer cmd(64, 16);
uint32_t left = SleepHandler::ticksToMs(SleepHandler::scheduledTicksLeft());
cmd += func;
cmd += "(";
cmd.appendSprintf("%lu", sleepMs);
Expand All @@ -684,7 +679,7 @@ void PinoccioScout::doSleep(bool pastEnd) {

uint32_t ret = Shell.eval((char*)cmd.c_str());

if (!left || !ret || sleepPending) {
if (!ret || !sleepPending) {
// If the callback returned false, or it scheduled a new sleep or
// we finished our previous sleep, then we're done with this
// callback.
Expand All @@ -694,7 +689,6 @@ void PinoccioScout::doSleep(bool pastEnd) {
// sleep interval, and we're not done sleeping yet, this means we
// should continue sleeping (though note that at least one loop
// cycle is ran before actually sleeping again).
sleepPending = true;
postSleepFunction = func;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Scout.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class PinoccioScout : public PinoccioClass {
uint32_t lastIndicate = 0;
void checkStateChange();

void doSleep(bool pastEnd);
void doSleep();

bool isVccEnabled;
bool isStateSaved;
Expand Down
72 changes: 32 additions & 40 deletions src/SleepHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,44 +73,32 @@ void SleepHandler::setup() {
PCICR |= (1 << PCIE0);
}

// Sleep until the timer match interrupt fired. If interruptible is
// true, this can return before if some other interrupt wakes us up
// from sleep. If this happens, true is returned.
bool SleepHandler::sleepUntilMatch(bool interruptible) {
while (true) {
#ifdef sleep_bod_disable
// On 256rfr2, BOD is automatically disabled in deep sleep, but
// some other MCUs need explicit disabling. This should happen shortly
// before actually sleeping. It's always automatically re-enabled.
sleep_bod_disable();
#endif
sei();
// AVR guarantees that the instruction after sei is executed, so
// there is no race condition here
sleep_cpu();
// Immediately disable interrupts again, to ensure that
// exactly one interrupt routine runs after wakeup, so
// we prevent race conditions and can properly detect if
// another interrupt than overflow occurred.
cli();
if (!timer_match && interruptible) {
// We were woken up, but the overflow interrupt
// didn't run, so another interrupt must have
// triggered. Note that if the overflow
// interrupt did trigger but not run yet, but also another
// (lower priority) interrupt occured, its flag will
// remain set and it will immediately wake us up
// on the next sleep attempt.
return false;
}
// See if overflow happened. Also check the IRQSCP3 flag,
// for the case where the overflow happens together with
// another (higher priority) interrupt.
if (timer_match || SCIRQS & (1 << IRQSCP3)) {
SCIRQS = (1 << IRQSCP3);
timer_match = true;
return true;
}
// Sleep until the timer match interrupt fired. This can return before
// if some other interrupt wakes us up from sleep. Consult timer_match
void SleepHandler::sleepUntilMatch() {
#ifdef sleep_bod_disable
// On 256rfr2, BOD is automatically disabled in deep sleep, but
// some other MCUs need explicit disabling. This should happen shortly
// before actually sleeping. It's always automatically re-enabled.
sleep_bod_disable();
#endif

sei();
// AVR guarantees that the instruction after sei is executed, so
// there is no race condition here
sleep_cpu();
// Immediately disable interrupts again, to ensure that
// exactly one interrupt routine runs after wakeup, so
// we prevent race conditions and can properly detect if
// another interrupt than overflow occurred.
cli();

// See if overflow happened. Also check the IRQSCP3 flag,
// for the case where the overflow happens together with
// another (higher priority) interrupt.
if (timer_match || SCIRQS & (1 << IRQSCP3)) {
SCIRQS = (1 << IRQSCP3);
timer_match = true;
}
}

Expand Down Expand Up @@ -147,7 +135,9 @@ uint32_t SleepHandler::scheduledTicksLeft() {
return left;
}

void SleepHandler::doSleep(bool interruptible) {
// prepares micro for sleep beforing calling internal sleep function
// returns the amount of ticks left to sleep if was prematurely woken
uint32_t SleepHandler::doSleep() {
// Disable Analag comparator
uint8_t acsr = ACSR;
ACSR = (1 << ACD);
Expand Down Expand Up @@ -242,7 +232,7 @@ void SleepHandler::doSleep(bool interruptible) {
SCIRQM |= (1 << IRQMCP3);

uint32_t before = read_sccnt();
sleepUntilMatch(interruptible);
sleepUntilMatch();
uint32_t after = read_sccnt();
totalSleep += (uint64_t)(after - before) * US_PER_TICK;

Expand All @@ -267,6 +257,8 @@ void SleepHandler::doSleep(bool interruptible) {
ACSR = acsr;
ADCSRA = adcsra;
while (!(ADCSRB & (1 << AVDDOK))) /* nothing */;

return scheduledTicksLeft();
}

void SleepHandler::setPinWakeup(uint8_t pin, bool enable) {
Expand Down
8 changes: 2 additions & 6 deletions src/SleepHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,9 @@ class SleepHandler {
// How many ticks are left before the end time is reached?
static uint32_t scheduledTicksLeft();

// Sleep until the previously scheduled time. If interruptible is
// true, this can return earlier if we are woken from sleep by
// another interrupt.
//
// If the previously scheduled end time has already passed, this
// returns immediately, without sleeping.
static void doSleep(bool interruptible);
static uint32_t doSleep();

static void setPinWakeup(uint8_t pin, bool enable);
static bool pinWakeupSupported(uint8_t pin);
Expand Down Expand Up @@ -88,7 +84,7 @@ class SleepHandler {
// Allow the symbol counter overflow ISR to access our protected members
friend void SCNT_OVFL_vect();

static bool sleepUntilMatch(bool interruptible);
static void sleepUntilMatch();
static uint32_t read_sccnt();
static void write_scocr3(uint32_t val);
static uint32_t read_scocr3();
Expand Down