Skip to content

Commit

Permalink
refactor existing sleep
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobrosenthal committed May 10, 2015
1 parent c44ca9d commit bf8963f
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 68 deletions.
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

0 comments on commit bf8963f

Please sign in to comment.