[ion] Improve suspend implementation

Change-Id: Ic43b58f34379292c53a82ab6c85674c6f7a9b381
This commit is contained in:
Émilie Feral
2017-04-12 15:15:03 +02:00
parent a0b054e2ec
commit c632f7c5ee
13 changed files with 145 additions and 120 deletions

View File

@@ -18,6 +18,7 @@ objs += $(addprefix ion/src/device/, \
sd_card.o\
swd.o \
usb.o \
wake_up.o \
)
# When using the register.h C++ file in production mode, we expect the compiler

View File

@@ -80,23 +80,6 @@ void shutdown() {
RCC.APB2ENR()->setADC1EN(false);
}
void generateWakeUpEventForChargingState() {
initGPIO();
/* Warning: pins with the same number in different groups cannot be set as
* source input for EXTI at the same time. Here, EXTICR1 register is filled
* between position 0-3 (charging pin = 0) with
* 0000 (ChargingGPIO = group A). */
SYSCFG.EXTICR1()->setEXTI(ChargingPin, ChargingGPIO);
EXTI.EMR()->set(ChargingPin, true);
/* We need to detect when the battery starts and stops charging. We set the
* wake up event on the falling and rising edge. */
EXTI.FTSR()->set(ChargingPin, true);
EXTI.RTSR()->set(ChargingPin, true);
}
}
}
}

View File

@@ -17,7 +17,6 @@ void init();
void shutdown();
void initGPIO();
void initADC();
void generateWakeUpEventForChargingState();
constexpr GPIO ChargingGPIO = GPIOA;
constexpr uint8_t ChargingPin = 0;

View File

@@ -115,6 +115,7 @@ void init() {
bool consolePeerConnectedOnBoot = Ion::Console::Device::peerConnected();
initPeripherals();
LED::Device::init();
if (consolePeerConnectedOnBoot) {
Ion::Device::Bench::run();
@@ -122,15 +123,16 @@ void init() {
}
void shutdown() {
shutdownPeripherals(false);
shutdownPeripherals();
shutdownClocks();
}
void initPeripherals() {
/* WARNING: it never inits the LED that is manually switch on/off in sleep
* mode when needed. */
Display::Device::init();
Backlight::Device::init();
Keyboard::Device::init();
LED::Device::init();
Battery::Device::init();
USB::Device::init();
#if USE_SD_CARD
@@ -140,7 +142,9 @@ void initPeripherals() {
SWD::Device::init();
}
void shutdownPeripherals(bool persitingLED) {
void shutdownPeripherals() {
/* WARNING: it never shutdowns the LED that can be switched on/off in sleep
* mode. */
SWD::Device::shutdown();
Console::Device::shutdown();
#if USE_SD_CARD
@@ -148,9 +152,6 @@ void shutdownPeripherals(bool persitingLED) {
#endif
USB::Device::shutdown();
Battery::Device::shutdown();
if (!persitingLED) {
LED::Device::shutdown();
}
Keyboard::Device::shutdown();
Backlight::Device::shutdown();
Display::Device::shutdown();
@@ -216,37 +217,29 @@ void initClocks() {
RCC.AHB3ENR()->setFSMCEN(true);
}
void initStandbyClock () {
// AHB1 bus
class RCC::AHB1ENR ahb1enr(0); // Reset value
ahb1enr.setGPIOAEN(true);
ahb1enr.setGPIOBEN(true);
ahb1enr.setGPIOCEN(true);
ahb1enr.setGPIOEEN(true);
RCC.AHB1ENR()->set(ahb1enr);
// APB1 bus
// We're using TIM3
RCC.APB1ENR()->setTIM3EN(true);
RCC.APB1ENR()->setPWREN(true);
void shutdownClocks() {
/* Reset values, everything off except LED timer clock */
// APB2 bus
class RCC::APB2ENR apb2enr(0x00008000); // Reset value
apb2enr.setSYSCFGEN(true);
RCC.APB2ENR()->set(apb2enr);
}
RCC.APB2ENR()->set(0x00008000); // Reset value
void shutdownClocks() {
// Reset values, everything off
RCC.APB2ENR()->set(0x00008000);
RCC.APB1ENR()->set(0x00000400);
/* AHB1
* TIM3 clock is needed on to drive the LED */
class RCC::APB1ENR apb1enr(0x00000400); // Reset value
apb1enr.setTIM3EN(true);
RCC.APB1ENR()->set(apb1enr);
// AHB1 bus
RCC.AHB1ENR()->set(0);
RCC.AHB1ENR()->set(0); // Reset value
RCC.AHB3ENR()->setFSMCEN(false);
}
void shutdownLEDClocks() {
/* AHB1 */
RCC.APB1ENR()->set(0x00000400); // Reset value
}
}
}

View File

@@ -9,10 +9,10 @@ void shutdown();
void initFPU();
void initPeripherals();
void shutdownPeripherals(bool persistingLED);
void shutdownPeripherals();
void initClocks();
void initStandbyClock();
void shutdownClocks();
void shutdownLEDClocks();
/* Pin | Role | Mode | Function
* -----+-------------------+-----------------------+----------

View File

@@ -117,33 +117,6 @@ void shutdown() {
}
}
void generateWakeUpEventForPowerKey() {
Key key = Key::B2;
uint8_t rowPin = RowPins[rowForKey(key)];
RowGPIO.MODER()->setMode(rowPin, GPIO::MODER::Mode::Output);
RowGPIO.OTYPER()->setType(rowPin, GPIO::OTYPER::Type::OpenDrain);
RowGPIO.ODR()->set(rowPin, 0);
uint8_t column = columnForKey(key);
uint8_t columnPin = ColumnPins[column];
ColumnGPIO.MODER()->setMode(columnPin, GPIO::MODER::Mode::Input);
ColumnGPIO.PUPDR()->setPull(columnPin, GPIO::PUPDR::Pull::Up);
/* Warning: pins with the same number in different groups cannot be set as
* source input for EXTI at the same time. Here, EXTICR1 register is filled
* between position 4-7 (column pin = 1) with 0010 (ColumnGPIO = group C). */
SYSCFG.EXTICR1()->setEXTI(columnPin, ColumnGPIO);
EXTI.EMR()->set(columnPin, true);
/* When the key is pressed, it will go from 1 (because it's pulled up) to
* zero (because it's connected to the open-drain output. In other words,
* we're waiting for a falling edge. */
EXTI.FTSR()->set(columnPin, true);
}
}
}
}

View File

@@ -45,8 +45,6 @@ inline uint8_t columnForKey(Key key) {
return (int)key%numberOfColumns;
}
void generateWakeUpEventForPowerKey();
}
}
}

View File

@@ -6,59 +6,62 @@
#include "keyboard.h"
#include "led.h"
#include "usb.h"
#include "wake_up.h"
void Ion::Power::suspend() {
Device::shutdownPeripherals(USB::isPlugged());
// Shutdown all peripherals except the LED
Device::shutdownPeripherals();
PWR.CR()->setLPDS(true); // Turn the regulator off. Takes longer to wake up.
PWR.CR()->setFPDS(true); // Put the flash to sleep. Takes longer to wake up.
CM4.SCR()->setSLEEPDEEP(true);
Device::shutdownClocks();
WakeUp::Device::onChargingEvent();
WakeUp::Device::onUSBPlugging();
msleep(300);
while (1) {
/* Update LEDS
* if the standby mode was stopped due to a "stop charging" event, we wait
* a while to be sure that the plug state of the USB is up-to-date. */
msleep(200);
KDColor LEDColor = KDColorBlack;
if (Battery::isCharging()) {
LEDColor = KDColorRed;
} else if (USB::isPlugged()) {
LEDColor = KDColorGreen;
} else {
LEDColor = KDColorBlack;
}
if (LEDColor != KDColorBlack) {
LED::Device::init();
} else {
LED::Device::shutdown();
}
LED::setColor(LEDColor);
/* To enter sleep, we need to issue a WFE instruction, which waits for the
WakeUp::Device::onPowerKeyDown();
Device::shutdownClocks();//Shutdown all except LED
if (LEDColor == KDColorBlack) {
Device::shutdownLEDClocks();
}
/* To enter sleep, we need to issue a WFE instruction, which waits for the
* event flag to be set and then clears it. However, the event flag might
* already be on. So the safest way to make sure we actually wait for a new
* event is to force the event flag to on (SEV instruction), use a first WFE
* to clear it, and then a second WFE to wait for a _new_ event. */
Device::initStandbyClock();
//TODO: optimize stand by consumption
while (1) {
Keyboard::Device::generateWakeUpEventForPowerKey();
Battery::Device::generateWakeUpEventForChargingState();
USB::Device::generateWakeUpEventForUSBPlug();
asm("sev");
asm("wfe");
msleep(1);
asm("wfe");
Device::initClocks();
Keyboard::Device::init();
Keyboard::State scan = Keyboard::scan();
Keyboard::Device::shutdown();
// Update LEDS
KDColor LEDColor = KDColorBlack;
if (Battery::isCharging()) {
LEDColor = KDColorOrange;
} else if (Ion::USB::isPlugged()) {
LEDColor = KDColorGreen;
} else {
LEDColor = KDColorBlack;
}
if (LEDColor == KDColorBlack) {
LED::setColor(LEDColor);
//TODO: understand why only shutdown the LED is inefficient
Device::shutdownPeripherals(false);
} else {
LED::Device::init();
LED::setColor(LEDColor);
}
Ion::Keyboard::State OnlyPowerKeyDown = (1 << (uint8_t)Keyboard::Key::B2);
if (scan == OnlyPowerKeyDown) {
// Wake up
@@ -69,4 +72,5 @@ void Ion::Power::suspend() {
Device::initClocks();
Device::initPeripherals();
LED::Device::init();
}

View File

@@ -73,6 +73,7 @@ public:
class APB1ENR : public Register32 {
public:
using Register32::Register32;
REGS_BOOL_FIELD(TIM3EN, 1);
REGS_BOOL_FIELD(SPI3EN, 15);
REGS_BOOL_FIELD(USART3EN, 18);

View File

@@ -33,17 +33,6 @@ void shutdown() {
VbusPin.group().PUPDR()->setPull(VbusPin.pin(), GPIO::PUPDR::Pull::None);
}
void generateWakeUpEventForUSBPlug() {
initGPIO();
/* Warning: pins with the same number in different groups cannot be set as
* source input for EXTI at the same time. Here, EXTICR3 register is
* filled between position 4-7 (Vbus pin = 9) with 0000 (Vbus GPIO = group A). */
SYSCFG.EXTICR3()->setEXTI(VbusPin.pin(), VbusPin.group());
EXTI.EMR()->set(VbusPin.pin(), true);
EXTI.FTSR()->set(VbusPin.pin(), true);
EXTI.RTSR()->set(VbusPin.pin(), true);
}
}
}
}

View File

@@ -15,7 +15,6 @@ namespace Device {
void init();
void initGPIO();
void shutdown();
void generateWakeUpEventForUSBPlug();
constexpr static GPIOPin VbusPin = GPIOPin(GPIOA, 9);

View File

@@ -0,0 +1,67 @@
#include "wake_up.h"
#include "regs/regs.h"
#include "battery.h"
#include "usb.h"
#include "keyboard.h"
namespace Ion {
namespace WakeUp {
namespace Device {
void onChargingEvent() {
Battery::Device::initGPIO();
/* Warning: pins with the same number in different groups cannot be set as
* source input for EXTI at the same time. Here, EXTICR1 register is filled
* between position 0-3 (charging pin = 0) with
* 0000 (ChargingGPIO = group A). */
SYSCFG.EXTICR1()->setEXTI(Battery::Device::ChargingPin, Battery::Device::ChargingGPIO);
EXTI.EMR()->set(Battery::Device::ChargingPin, true);
/* We need to detect when the battery stops charging. We set the
* wake up event on the rising edge. */
EXTI.RTSR()->set(Battery::Device::ChargingPin, true);
}
void onUSBPlugging() {
USB::Device::initGPIO();
/* Here, EXTICR3 register is filled between position 4-7 (Vbus pin = 9) with
* 0000 (Vbus GPIO = group A). */
SYSCFG.EXTICR3()->setEXTI(USB::Device::VbusPin.pin(), USB::Device::VbusPin.group());
EXTI.EMR()->set(USB::Device::VbusPin.pin(), true);
EXTI.FTSR()->set(USB::Device::VbusPin.pin(), true);
EXTI.RTSR()->set(USB::Device::VbusPin.pin(), true);
}
void onPowerKeyDown() {
Keyboard::Key key = Keyboard::Key::B2;
uint8_t rowPin = Keyboard::Device::RowPins[Keyboard::Device::rowForKey(key)];
Keyboard::Device::RowGPIO.MODER()->setMode(rowPin, GPIO::MODER::Mode::Output);
Keyboard::Device::RowGPIO.OTYPER()->setType(rowPin, GPIO::OTYPER::Type::OpenDrain);
Keyboard::Device::RowGPIO.ODR()->set(rowPin, 0);
uint8_t column = Keyboard::Device::columnForKey(key);
uint8_t columnPin = Keyboard::Device::ColumnPins[column];
Keyboard::Device::ColumnGPIO.MODER()->setMode(columnPin, GPIO::MODER::Mode::Input);
Keyboard::Device::ColumnGPIO.PUPDR()->setPull(columnPin, GPIO::PUPDR::Pull::Up);
/* Here, EXTICR1 register is filled between position 4-7 (column pin = 1) with
* 0010 (ColumnGPIO = group C). */
SYSCFG.EXTICR1()->setEXTI(columnPin, Keyboard::Device::ColumnGPIO);
EXTI.EMR()->set(columnPin, true);
/* When the key is pressed, it will go from 1 (because it's pulled up) to
* zero (because it's connected to the open-drain output. In other words,
* we're waiting for a falling edge. */
EXTI.FTSR()->set(columnPin, true);
}
}
}
}

18
ion/src/device/wake_up.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef ION_DEVICE_WAKE_UP_H
#define ION_DEVICE_WAKE_UP_H
#include "regs/regs.h"
namespace Ion {
namespace WakeUp {
namespace Device {
void onChargingEvent();
void onUSBPlugging();
void onPowerKeyDown();
}
}
}
#endif