0

Using code similar to these examples from AVR

https://www.microchip.com/webdoc/AVRLibcReferenceManual/group__avr__sleep.html

my program causes my Adafruit Feather 32u4 to often sleep. When the firmware has nothing to do it will sleep about 75% of the time. Typically after several hours of operation there will be corruption on the LCD display. The elapsed time to the corruption is not deterministic.

When it sleeps or wakes up from sleep I believe the processor sends garbage to the LCD module via the SPI (serial) lines. I have no oscilloscope to observe this but the problem seems to be removed if I comment out the sleep code.

A description of the Adafruit ST7565 LCD module is at this link

https://www.adafruit.com/product/250.

My Arduino setup code relevant to sleeping is this:

cli(); // atomic operation begins here
WDTCSR = bit(WDCE) | bit(WDE); // enter a very brief change mode
WDTCSR = bit(WDP2); // prescaler without reset mode, 1001 means 8 seconds, 0101 means 0.5 seconds, 0100 means 0.25 seconds, 0011 means 0.125 seconds
WDTCSR = bit(WDIE); // use interrrupt mode because we do not want an WDT triggered reset
sei(); // global interrupt enable
set_sleep_mode(SLEEP_MODE_PWR_DOWN);

My sleep code in the Arduino loop is adapted from the AVR webpage and is invoked if the firmware is not busy, is as follows:

cli(); // atomic operation begins here
sleep_enable();
sei();
sleep_cpu();
sleep_disable();

The watchdog ISR for when it wakes up is an empty function, as follows:

ISR(WDT_vect) // need this to avoid a reset
{ // no code here
}

I have a little bit of other evidence that some lines are uncontrolled when the processor wakes up. The evidence comes from my rotary encoders. I see false encoder activity sometimes when the processor wakes up and I eliminated this problem by putting some "debounce code" in the watchdog ISR. I also make sure the encoder lines use the processor's internal pull-up resistors. Note that I already have debounce code for the encoders for the purpose of doing actual debouncing of the activity of actually touching the encoders. The additional "debounce code" in the watchdog ISR is for the situation in which I am not even touching the encoders but the processor is waking up and the lines are noisy. The encoder "debounce code" is not shown in the watchdog ISR code snippet because it's not relevant to my LCD problem.

The pull-up and debounce remedy for encoder input lines is not applicable to my LCD problem because the LCD involves output lines.

Edit: I tried to solve it by setting high the active low chip select of the LCD before sleep and then reverting the line upon waking up (in the watchdog ISR). This had the effect of making the display very sluggish. I update the display once per second and it seemed to be updating only once every 2 or 3 seconds. I have not analyzed whether my "direct intervention" is incompatible with the Adafruit library for this LCD.

The LCD library: https://github.com/adafruit/ST7565-LCD

The boards are as pictured. The encoder board has 2 encoders wired up and 2 encoders unused. The encoder board sits on a 3mm thick piece of foam and is clamped to the desk. The breadboard is attached to the desk via the double sided foam tape that seems to be standard on this item.

picture Addendum: Given Nick Gammon's answer, I will run with the following to see if the problem is eliminated. Since the corruption is not deterministic, it may take many hours or days before I form an opinion.

  cli(); // atomic operation begins here
  WDTCSR = bit(WDCE); // enter a brief change mode 
  WDTCSR = bit(WDIE) | bit(WDP2); // 1001=8s, 0101=0.5s, 0100=0.25s, 0011=0.125s
  sei(); // global interrupt enable
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); 

After less than 10 hours there was corruption on the LCD. I will test with the following.

  cli(); // atomic operation begins here
  WDTCSR = bit(WDCE) | bit(WDE); // enter a brief change mode 
  WDTCSR = bit(WDIE) | bit(WDP2); // 1001=8s, 0101=0.5s, 0100=0.25s, 0011=0.125s
  sei(); // global interrupt enable
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); 

The above still failed after several hours.

2
WDTCSR = bit(WDCE) | bit(WDE); // enter a very brief change mode
WDTCSR = bit(WDP2); // prescaler without reset mode, 1001 means 8 seconds, 0101 means 0.5 seconds, 0100 means 0.25 seconds, 0011 means 0.125 seconds
WDTCSR = bit(WDIE); // use interrrupt mode because we do not want an WDT triggered reset

Your code above is not doing a WDT interrupt every 0.25 seconds as you appear to be wanting, because you assign (not OR) in the third line. That means WDP0/WDP1/WDP2?WDP3 will all be zero which means the watchdog will fire every 16 ms.

This had the effect of making the display very sluggish.

I'm not totally surprised, if you interrupt every 16 ms.


I have a little bit of other evidence that some lines are uncontrolled when the processor wakes up.

The lines should remain what they were set to when the processor sleeps. It wouldn't be very useful to sleep if the processor started outputting random junk while asleep. I can't comment further as I can only see part of your code.

  • Thank you. My comment will be an addendum to the question. – H2ONaCl Jan 9 at 22:22
  • The encoder problem is strongly suggestive of noisy lines. I have 2 encoders wired up and I have seen spurious effects on both so if it is a manufacturing defect it is in both. A common cause could be vibration but that seems unlikely since the main source would be from my keyboarding on the same desk that the Arduino circuit is clamped to and while I do type hard I am hardly the worst keyboard abuser. – H2ONaCl Jan 10 at 6:56

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.