I'm trying to get a power down mode to work on the SAM7A3. Right now I
have it enter a power down state, where I configure all of the pins
controllable by the PIO, and then I set a bunch of them to certain
positions, configure the pull-ups on them, etc. I turn of the AIC, PMC
etc etc. I then configure the AIC to fire an interrupt on a level
change (when I push an external button) so that it will turn back on.
Lastly, I lower the clock frequency to 500 Hz, switch the master clock
to slow clock, then I disable the PLL, the Main Osciallator and then the
Processor Clock. All of this seems to work as its supposed to.
The issue arrises when I try to turn everything back on. I successfully
fire the interrupt, I'm able to turn the clock back on (or so the power
jump implies, as does the fact that the external oscillator starts
churning out 3.6 MHz again), but then my program just seems to kind of
hang... So I figured since I've screwed with all the PIO stuff and all
the peripherals, the best option to get everything back into shape is to
simply reset the entire damn board. Sounds easy right? Well, the reset
controller didn't seem to do jack, so I decided to try to jump to vector
0x0 and cause a reset.
Shouldn't this cause it to branch to 0x0, causing a reset?
__asm("mov r0, #0");
__asm("bx r0")
Here is all of my on/off code.
void hwSleep(void)
{
// Configure all pins as PIO controlled
AT91C_BASE_PIOA->PIO_PER = 0xFFFFFFFF;
AT91C_BASE_PIOB->PIO_PER = 0xFFFFFFFF;
// Disable USART RX, TX and it
AT91F_US_DisableRx(AT91C_BASE_US0);
AT91F_US_DisableTx(AT91C_BASE_US0);
AT91F_US_Close(AT91C_BASE_US0);
// Set LED pins to input with no pull-ups
AT91F_PIO_CfgInput(AT91C_BASE_PIOA, LED_PINS);
AT91C_BASE_PIOA->PIO_PPUDR = LED_PINS;
// Drive PA1 low, PA0 high (both high works better)
AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, AT91C_PIO_PA0 | AT91C_PIO_PA1);
AT91F_PIO_SetOutput(AT91C_BASE_PIOA, AT91C_PIO_PA0 | AT91C_PIO_PA1);
AT91C_BASE_PIOA->PIO_PPUDR = AT91C_PIO_PA0 | AT91C_PIO_PA1;
// SPI stuff
AT91F_SPI_Disable(AT91C_BASE_SPI0);
AT91F_SPI_Close(AT91C_BASE_SPI0);
U32 nPins = AT91C_PIO_PA11 | AT91C_PIO_PA12 | AT91C_PIO_PA13 |
AT91C_PIO_PA14 |
AT91C_PIO_PA15 | AT91C_PIO_PA16 | AT91C_PIO_PA17;
// Set PA11-17 to inputs (no pull-ups)
AT91F_PIO_CfgInput(AT91C_BASE_PIOA, nPins);
AT91C_BASE_PIOA->PIO_PPUDR = nPins;
// Set PB25 to input (no pull-up)
AT91F_PIO_CfgInput(AT91C_BASE_PIOB, AT91C_PIO_PB25);
AT91C_BASE_PIOB->PIO_PPUDR = AT91C_PIO_PB25;
nPins = AT91C_PIO_PB0 | AT91C_PIO_PB1 | AT91C_PIO_PB2 | AT91C_PIO_PB3
| AT91C_PIO_PB4 | AT91C_PIO_PB5 |
AT91C_PIO_PB6 | AT91C_PIO_PB7 | AT91C_PIO_PB8 | AT91C_PIO_PB9 |
AT91C_PIO_PB10 | AT91C_PIO_PB11 |
AT91C_PIO_PB12 | AT91C_PIO_PB13 | AT91C_PIO_PB14 | AT91C_PIO_PB15 |
AT91C_PIO_PB16;
// Set PB0-16 to input (no pull-up)
AT91F_PIO_CfgInput(AT91C_BASE_PIOB, nPins);
AT91C_BASE_PIOB->PIO_PPUDR = nPins;
// Set PB17-23 to input with (pull-up ENABLED)
AT91F_PIO_CfgInput(AT91C_BASE_PIOB, BTN_PINS);
AT91C_BASE_PIOB->PIO_PPUER = BTN_PINS;
// Enable the USB, then disable it. It helps
AT91C_BASE_PMC->PMC_PCER= AT91C_ID_UDP;
AT91C_BASE_PMC->PMC_PCDR= AT91C_ID_UDP;
// Disable all interrupts
AT91F_AIC_DisableIt(AT91C_BASE_AIC, 0xFFFFFFFF);
// Disable entire PMC except for GPIOB so we can be woken up
AT91C_BASE_PMC->PMC_PCDR = 0xFFFFFFF7;
AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC, 1 << AT91C_ID_PIOB);
// Drive PB24 High (MUST BE LAST)
AT91F_PIO_CfgOutput(AT91C_BASE_PIOB, AT91C_PIO_PB24);
AT91F_PIO_SetOutput(AT91C_BASE_PIOB, AT91C_PIO_PB24);
// Configure the AIC to fire off an interrupt on PB17 (active low)
AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_PIOB,
INTERRUPT_PRIORITY_SEVEN, AT91C_AIC_SRCTYPE_EXT_LOW_LEVEL,
hwWakeUpInterrupt);
AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_PIOB);
// Read the Status Register to clear all pending interrupts
nPins = AT91C_BASE_PIOB->PIO_ISR;
// Enable the PIO Interrupt
AT91C_BASE_PIOB->PIO_IER = AT91C_PIO_PB17;
// Reduce the MCK Frequency Down to 500 Hz
nTemp = 0;
nTemp |= AT91C_PMC_PRES_CLK_64;
AT91C_BASE_PMC->PMC_MCKR = nTemp ;
while(!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY));
// Switch the Master Clock (MCK) to Slow Clock
nTemp = 0;
AT91C_BASE_PMC->PMC_MCKR = nTemp ;
while(!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY));
// Enable the Shutdown pin
AT91C_BASE_SHDWC->SHDWC_SHCR = AT91C_SHDWC_KEY | AT91C_SHDWC_SHDW;
AT91C_BASE_SHDWC->SHDWC_SHMR = AT91C_SHDWC_WKMODE0_NONE |
AT91C_SHDWC_WKMODE1_NONE;
// Disable the PLL
AT91C_BASE_PMC->PMC_PLLR = 0x0;
// Disable the Main Oscillator
AT91C_BASE_PMC->PMC_MOR = 0x0;
// Disable Processor Clock and USB clock
AT91C_BASE_PMC->PMC_SCDR = 0xFFFF;
}
void hwWakeUpInterrupt(void)
{
// Read the Status Register to clear all pending interrupts
U32 nTemp = AT91C_BASE_PIOB->PIO_ISR;
nTemp = 0;
// Disable the PIO Interrupt
AT91C_BASE_PIOB->PIO_IDR = AT91C_PIO_PB17;
__asm("mov r0, #0");
__asm("bx r0");
// Turn all the clocks back on;
AT91F_LowLevelInit();
// Acknowledge the interrupt
AT91F_AIC_AcknowledgeIt(AT91C_BASE_AIC);
}
Note: The LowLevelInit file is the file that configures the clock and
default interrupt handler vectors that the assembly file calls.