I've run into quite the problem with my 32u4, I can't get it to reconnect to the computer upon wakeup from a sleep mode!
So I have an Arduino Pro Micro featuring the 32u4, and I'm also designing a project with a standalone 32u4 (I'm using the Pro Micro as a test platform before I begin building my project). It is a battery operated device with a soft-start, so I need power consumption to be a miniscule as possible (hence the sleep modes).
After learning about the registers and brushing up on some binary, putting the chip to sleep was fairly straight forward, however, I cannot get the USB interface internal to the 32u4 to 'wake back up' or reconnect to the computer. I've attached my code below. I also tested this code on my Arduino Nano and it works just fine, however with added USB features of the 32u4, I cannot get it to reconnect to the computer, and I have to reset the chip a million times to get the IDE to finally recognise it and upload something else to it.
Here is my lovely code below. All it does it upon startup, go sleep, and when interrupt on pin 2 goes low, wake the chip back up and run the blink sketch. It goes to sleep, and wakes up and runs the sketch, it just won't ever reconnect to my computer, until I do a hardware reset.
int btn1Pin = 2;
int led1 = 9;
// these are the 'standard' register configurations
byte _SMCR = 0; // disable sleep mode
byte _ADCSRA = 0b10000111; // re-enable ADC
int interval = 500;
int ledState = true;
unsigned long ct1 = 0;
unsigned long pt1 = 0;
void setup()
{
pinMode(led1, OUTPUT);
attachInterrupt(digitalPinToInterrupt(btn1Pin), wakeUp, FALLING);
sleep();
}
void loop()
{
ct1 = millis(); // blink something so I know you're working
if (ct1 - pt1 >= interval)
{
digitalWrite(led1, ledState);
ledState = !ledState;
pt1 = ct1;
}
}
void wakeUp()
{
SMCR = _SMCR; // disable sleep mode
ADCSRA = _ADCSRA; // re-enable ADC
}
void sleep()
{
ADCSRA &= ~(1 << 7); // disable ADC
SMCR |= ( 1<<2 ); // power down mode
SMCR |= 1; // enable sleep
__asm__ __volatile__("sleep"); // hypnotist snaps fingers, chip is out cold!
}
Inside AJ's brain (more research): 32u4 Datasheet for quick reference
I added a quick led turn on in the wakeUp() ISR to ensure it was actually getting executed, and indeed it was.
The description of the USBCON register (Page 267 - 21.13.1) claims that clearing bit 7 disables and resets the USB controller. I tried another iteration of code where in sleep() I cleared bit 7, hoping that reset the controller, and in wakeUp() I set the bit to turn it back on after reset, but that didn't work. In fact, it gave me a really tough time when trying to reprogram (even though I KNOW that the wakeUp() ISR is executing and the bit had to have been reset).
After some more reading, I saw (Page 262 - 21.7.2) says that in power down mode (the sleep mode I'm using), "the oscillator is stopped and halts all the clocks (CPU and peripherals). The USB controller 'wakes up' when:
- the WAKEUPI interrupt is triggered
- the VBUSTI interrupt is triggered."
Okay so this makes sense, in power down mode the USB controller is stopped due to the lack of an oscillator, cool! I understand that to mean I need to trigger one of those interrupts to re-enable the USB controller. So I searched around for the first interrupt "WAKEUPI", and it appears this bit is set by hardware (Page 282), I'm guessing this means when you plug in the USB cable? I wrote a sketch to read that register, and it returned with 00010000, so I'm assumed the above statement is true. It also says setting it with software has no effect, so I left this one to explore the next interrupt, VBUSTI, which seems to be part of the automatic USB plug in detection based off of the VBUS pad. Maybe something here needs to be changed but I can't tell.
Page 266 - 21.12 talks about software USB operating modes, and the last one is "Resuming the USB interface." The 4 bullets provide to wake up are as such:
- Enable PLL
- Wait PLL lock
- Unfreeze USB clock
- Clear Resume information
I tried to do all of the above by adding this code to the wakeUp() ISR. It still didn't work :(. I know it's brutal and ISRs should be as short as possible but I gave it a go anyways.
PLLCSR = _PLLCSR; // enable PLL clock
var = bitRead(PLLCSR, 0);
while (var != 1) {} // wait for clock to lock
USBCON = _USBCON; // unfreeze USB clock
UDINT = 0; // clear resume flag
Maybe I'm making all of this more complicated than it needs to be, I don't know! I can't get this thing to reconnect.