From 3725c03a175c5b274384a80c4de02d7b11ddc9ef Mon Sep 17 00:00:00 2001
From: Terraneo Federico <fede.tft@hotmail.it>
Date: Tue, 20 Aug 2013 18:22:50 +0200
Subject: [PATCH] Improving BSP for sony watch

---
 .../common/drivers/stm32_hardware_rng.cpp     |  13 +-
 .../arch/common/drivers/stm32_hardware_rng.h  |   7 +
 miosix/arch/common/drivers/stm32f2_f4_i2c.cpp | 322 ++++++++++++++++++
 miosix/arch/common/drivers/stm32f2_f4_i2c.h   |  77 +++++
 .../interfaces-impl/bsp.cpp                   | 222 +++++++++++-
 .../interfaces-impl/bsp_impl.h                | 123 ++++++-
 miosix/config/Makefile.inc                    |   1 +
 miosix_np_2/nbproject/configurations.xml      |  20 ++
 .../nbproject/private/configurations.xml      |   2 +
 miosix_np_2/nbproject/private/private.xml     |   2 +-
 10 files changed, 780 insertions(+), 9 deletions(-)
 create mode 100644 miosix/arch/common/drivers/stm32f2_f4_i2c.cpp
 create mode 100644 miosix/arch/common/drivers/stm32f2_f4_i2c.h

diff --git a/miosix/arch/common/drivers/stm32_hardware_rng.cpp b/miosix/arch/common/drivers/stm32_hardware_rng.cpp
index 3f3773e3..9595e24d 100644
--- a/miosix/arch/common/drivers/stm32_hardware_rng.cpp
+++ b/miosix/arch/common/drivers/stm32_hardware_rng.cpp
@@ -29,9 +29,10 @@
 #include "interfaces/delays.h"
 #include "stm32_hardware_rng.h"
 
-using namespace miosix;
 using namespace std;
 
+namespace miosix {
+
 //
 // class HardwareRng
 //
@@ -91,7 +92,11 @@ void HardwareRng::get(void* buf, unsigned int size)
 
 unsigned int HardwareRng::getImpl()
 {
-    for(int i=0;i<16;i++)
+    #ifndef __NO_EXCEPTIONS
+    for(int i=0;i<16;i++) //Try up to a reasonable # of times, then throw
+    #else
+    for(;;) //Can't return an error, keep retrying
+    #endif
     {
         int timeout=1000000;
         unsigned int sr;
@@ -108,5 +113,9 @@ unsigned int HardwareRng::getImpl()
         old=result;
         return result;
     }
+    #ifndef __NO_EXCEPTIONS
     throw runtime_error("RNG Fault detected");
+    #endif //__NO_EXCEPTIONS
 }
+
+} //namespace miosix
diff --git a/miosix/arch/common/drivers/stm32_hardware_rng.h b/miosix/arch/common/drivers/stm32_hardware_rng.h
index 536af82a..d3ea2a50 100644
--- a/miosix/arch/common/drivers/stm32_hardware_rng.h
+++ b/miosix/arch/common/drivers/stm32_hardware_rng.h
@@ -31,6 +31,8 @@
 #include "interfaces/arch_registers.h"
 #include "kernel/sync.h"
 
+namespace miosix {
+
 /**
  * Class to access the hardware random number generator in Miosix
  * Works with the hardware RNG in stm32f2 and stm32f4
@@ -58,6 +60,9 @@ public:
     void get(void *buf, unsigned int size);
     
 private:
+    HardwareRng(const HardwareRng&);
+    HardwareRng& operator=(const HardwareRng&);
+    
     /**
      * Constructor
      */
@@ -87,4 +92,6 @@ private:
     unsigned int old; ///< Previously read value
 };
 
+} //namespace miosix
+
 #endif //RNG_H
diff --git a/miosix/arch/common/drivers/stm32f2_f4_i2c.cpp b/miosix/arch/common/drivers/stm32f2_f4_i2c.cpp
new file mode 100644
index 00000000..11fa9991
--- /dev/null
+++ b/miosix/arch/common/drivers/stm32f2_f4_i2c.cpp
@@ -0,0 +1,322 @@
+
+#include "stm32f2_f4_i2c.h"
+#include <miosix.h>
+#include <kernel/scheduler/scheduler.h>
+
+using namespace miosix;
+
+static bool error;        ///< Set to true by IRQ on error
+static Thread *waiting=0; ///< Thread waiting for an operation to complete
+
+/**
+ * DMA I2C rx end of transfer
+ */
+void __attribute__((naked)) DMA1_Stream0_IRQHandler()
+{
+    saveContext();
+    asm volatile("bl _Z20I2C1rxDmaHandlerImplv");
+    restoreContext();
+}
+
+/**
+ * DMA I2C tx end of transfer actual implementation
+ */
+void __attribute__((used)) I2C1rxDmaHandlerImpl()
+{
+    DMA1->LIFCR=DMA_LIFCR_CTCIF0
+              | DMA_LIFCR_CTEIF0
+              | DMA_LIFCR_CDMEIF0
+              | DMA_LIFCR_CFEIF0;
+    I2C1->CR1 |= I2C_CR1_STOP;
+    if(waiting==0) return;
+    waiting->IRQwakeup();
+    if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+        Scheduler::IRQfindNextThread();
+    waiting=0;
+}
+
+/**
+ * DMA I2C tx end of transfer
+ */
+void DMA1_Stream7_IRQHandler()
+{
+    DMA1->HIFCR=DMA_HIFCR_CTCIF7
+              | DMA_HIFCR_CTEIF7
+              | DMA_HIFCR_CDMEIF7
+              | DMA_HIFCR_CFEIF7;
+    
+    //We can't just wake the thread because the I2C is double buffered, and this
+    //interrupt is fired at the same time as the second last byte is starting
+    //to be sent out of the bus. If we return now, the main code would send a
+    //stop condiotion too soon, and the last byte would never be sent. Instead,
+    //we change from DMA mode to IRQ mode, so when the second last byte is sent,
+    //that interrupt is fired and the last byte is sent out.
+    //Note that since no thread is awakened from this IRQ, there's no need for
+    //the saveContext(), restoreContext() and __attribute__((naked))
+    I2C1->CR2 &= ~I2C_CR2_DMAEN;
+    I2C1->CR2 |= I2C_CR2_ITBUFEN | I2C_CR2_ITEVTEN;
+}
+
+/**
+ * I2C address sent interrupt
+ */
+void __attribute__((naked)) I2C1_EV_IRQHandler()
+{
+    saveContext();
+    asm volatile("bl _Z15I2C1HandlerImplv");
+    restoreContext();
+}
+
+/**
+ * I2C address sent interrupt actual implementation
+ */
+void __attribute__((used)) I2C1HandlerImpl()
+{
+    //When called to resolve the last byte not sent issue, clearing
+    //I2C_CR2_ITBUFEN prevents this interrupt being re-entered forever, as
+    //it does not send another byte to the I2C, so the interrupt would remain
+    //pending. When called after the start bit has been sent, clearing
+    //I2C_CR2_ITEVTEN prevents the same infinite re-enter as this interrupt
+    //does not start an address transmission, which is necessary to stop
+    //this interrupt from being pending
+    I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN);
+    if(waiting==0) return;
+    waiting->IRQwakeup();
+    if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+        Scheduler::IRQfindNextThread();
+    waiting=0;
+}
+
+/**
+ * I2C error interrupt
+ */
+void __attribute__((naked)) I2C1_ER_IRQHandler()
+{
+    saveContext();
+    asm volatile("bl _Z18I2C1errHandlerImplv");
+    restoreContext();
+}
+
+/**
+ * I2C error interrupt actual implementation
+ */
+void __attribute__((used)) I2C1errHandlerImpl()
+{
+    I2C1->SR1=0; //Clear error flags
+    error=true;
+    if(waiting==0) return;
+    waiting->IRQwakeup();
+    if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+        Scheduler::IRQfindNextThread();
+    waiting=0;
+}
+
+namespace miosix {
+
+//
+// class I2C
+//
+
+I2C1Driver& I2C1Driver::instance()
+{
+    static I2C1Driver singleton;
+    return singleton;
+}
+
+void I2C1Driver::init()
+{
+    //I2C devices are connected to APB1, whose frequency is the system clock
+    //divided by a value set in the PPRE1 bits of RCC->CFGR
+    const int ppre1=(RCC->CFGR & RCC_CFGR_PPRE1)>>10;
+    const int divFactor= (ppre1 & 1<<2) ? (2<<(ppre1 & 0x3)) : 1;
+    const int fpclk1=SystemCoreClock/divFactor;
+    //iprintf("fpclk1=%d\n",fpclk1);
+    
+    {
+        FastInterruptDisableLock dLock;
+        RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
+        RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; //Enable clock gating
+    }
+    
+    NVIC_SetPriority(DMA1_Stream7_IRQn,10);//Low priority for DMA
+    NVIC_ClearPendingIRQ(DMA1_Stream7_IRQn);//DMA1 stream 7 channel 1 = I2C1 TX 
+    NVIC_EnableIRQ(DMA1_Stream7_IRQn);
+    
+    NVIC_SetPriority(DMA1_Stream0_IRQn,10);//Low priority for DMA
+    NVIC_ClearPendingIRQ(DMA1_Stream0_IRQn);//DMA1 stream 0 channel 1 = I2C1 RX 
+    NVIC_EnableIRQ(DMA1_Stream0_IRQn);
+    
+    NVIC_SetPriority(I2C1_EV_IRQn,10);//Low priority for I2C
+    NVIC_ClearPendingIRQ(I2C1_EV_IRQn);
+    NVIC_EnableIRQ(I2C1_EV_IRQn);
+    
+    NVIC_SetPriority(I2C1_ER_IRQn,10);
+    NVIC_ClearPendingIRQ(I2C1_ER_IRQn);
+    NVIC_EnableIRQ(I2C1_ER_IRQn);
+    
+    I2C1->CR1=I2C_CR1_SWRST;
+    I2C1->CR1=0;
+    I2C1->CR2=fpclk1/1000000; //Set pclk frequency in MHz
+    //This sets the duration of both Thigh and Tlow (master mode))
+    const int spiSpeed=100000; //100KHz
+    I2C1->CCR=std::max(4,fpclk1/(2*spiSpeed)); //Duty=2, standard mode (100KHz)
+    //Datasheet says with I2C @ 100KHz, maximum SCL rise time is 1000ns
+    //Need to change formula if I2C needs to run @ 400kHz
+    I2C1->TRISE=fpclk1/1000000+1;
+    I2C1->CR1=I2C_CR1_PE; //Enable peripheral
+}
+
+bool I2C1Driver::send(unsigned char address, const void *data, int len)
+{
+    address &= 0xfe; //Mask bit 0, as we are writing
+    if(start(address)==false || (I2C1->SR2 & I2C_SR2_TRA)==0)
+    {
+        I2C1->CR1 |= I2C_CR1_STOP;
+        return false;
+    }
+
+    error=false;
+    waiting=Thread::getCurrentThread();
+    DMA1_Stream7->CR=0;
+    DMA1_Stream7->PAR=reinterpret_cast<unsigned int>(&I2C1->DR);
+    DMA1_Stream7->M0AR=reinterpret_cast<unsigned int>(data);
+    DMA1_Stream7->NDTR=len;
+    DMA1_Stream7->FCR=DMA_SxFCR_FEIE
+                    | DMA_SxFCR_DMDIS;
+    DMA1_Stream7->CR=DMA_SxCR_CHSEL_0 //Channel 1
+                   | DMA_SxCR_MINC    //Increment memory pointer
+                   | DMA_SxCR_DIR_0   //Memory to peripheral
+                   | DMA_SxCR_TCIE    //Interrupt on transfer complete
+                   | DMA_SxCR_TEIE    //Interrupt on transfer error
+                   | DMA_SxCR_DMEIE   //Interrupt on direct mode error
+                   | DMA_SxCR_EN;     //Start DMA
+    
+    //Enable DMA in the I2C peripheral *after* having configured the DMA
+    //peripheral, or a spurious interrupt is triggered
+    I2C1->CR2 |= I2C_CR2_DMAEN | I2C_CR2_ITERREN;
+ 
+    {
+        FastInterruptDisableLock dLock;
+        while(waiting)
+        {
+            waiting->IRQwait();
+            {
+                FastInterruptEnableLock eLock(dLock);
+                Thread::yield();
+            }
+        }
+    }
+    
+    DMA1_Stream7->CR=0;
+    
+    //The DMA interrupt routine changes the interrupt flags!
+    I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN);
+    
+    /*
+     * The main idea of this driver is to avoid having the processor spinning
+     * waiting on some status flag. Why? Because I2C is slow compared to a
+     * modern processor. A 120MHz core does 1200 clock cycles in the time it
+     * takes to transfer a single bit through an I2C clocked at 100KHz.
+     * This time could be better spent doing a context switch and letting
+     * another thread do useful work, or (and Miosix does it automatically if
+     * there are no ready threads) sleeping the processor core. However,
+     * I'm quite disappointed by the STM32 I2C peripheral, as it seems overly
+     * complicated to use. To come close to achieving this goal I had to
+     * orchestrate among *four* interrupt handlers, two of the DMA, and two
+     * of the I2C itself. And in the end, what's even more disappointing, is
+     * that I haven't found a way to completely avoid spinning. Why?
+     * There's no interrupt that's fired when the stop bit is sent!
+     * And what's worse, the documentation says that after you set the stop
+     * bit in the CR2 register you can't write to it again (for example, to send
+     * a start bit because two i2c api calls are made back to back) until the
+     * MSL bit is cleared. But there's no interrupt tied to that event!
+     * What's worse, is that the closest interrupt flag I've found when doing
+     * an I2C send is fired when the last byte is *beginning* to be sent.
+     * Maybe I haven't searched well enough, but the fact is I found nothing,
+     * so this code below spins for 8 data bits of the last byte plus the ack
+     * bit, plus the stop bit. That's 12000 wasted CPU cycles. Thanks, ST...
+     */
+    I2C1->CR1 |= I2C_CR1_STOP;
+    while(I2C1->SR2 & I2C_SR2_MSL) ; //Wait for stop bit sent
+    return !error;
+}
+
+bool I2C1Driver::recv(unsigned char address, void *data, int len)
+{
+    address |= 0x01;
+    if(start(address,len==1)==false || I2C1->SR2 & I2C_SR2_TRA)
+    {
+        I2C1->CR1 |= I2C_CR1_STOP;
+        return false;
+    }
+
+    I2C1->CR2 |= I2C_CR2_DMAEN | I2C_CR2_LAST | I2C_CR2_ITERREN;
+    
+    error=false;
+    waiting=Thread::getCurrentThread();
+    DMA1_Stream0->CR=0;
+    DMA1_Stream0->PAR=reinterpret_cast<unsigned int>(&I2C1->DR);
+    DMA1_Stream0->M0AR=reinterpret_cast<unsigned int>(data);
+    DMA1_Stream0->NDTR=len;
+    DMA1_Stream0->FCR=DMA_SxFCR_FEIE
+                    | DMA_SxFCR_DMDIS;
+    DMA1_Stream0->CR=DMA_SxCR_CHSEL_0 //Channel 1
+                   | DMA_SxCR_MINC    //Increment memory pointer
+                   | DMA_SxCR_TCIE    //Interrupt on transfer complete
+                   | DMA_SxCR_TEIE    //Interrupt on transfer error
+                   | DMA_SxCR_DMEIE   //Interrupt on direct mode error
+                   | DMA_SxCR_EN;     //Start DMA
+
+    {
+        FastInterruptDisableLock dLock;
+        while(waiting)
+        {
+            waiting->IRQwait();
+            {
+                FastInterruptEnableLock eLock(dLock);
+                Thread::yield();
+            }
+        }
+    }
+
+    DMA1_Stream7->CR=0;
+    
+    I2C1->CR2 &= ~(I2C_CR2_DMAEN | I2C_CR2_LAST | I2C_CR2_ITERREN);
+    while(I2C1->SR2 & I2C_SR2_MSL) ; //Wait for stop bit sent
+    return !error;
+}
+
+bool I2C1Driver::start(unsigned char address, bool immediateNak)
+{
+    I2C1->CR1 |= I2C_CR1_START | I2C_CR1_ACK;
+    if(!waitStatus1()) return false;
+    if((I2C1->SR1 & I2C_SR1_SB)==0) return false; //Must read SR1 to clear flag
+    I2C1->DR=address;
+    if(immediateNak) I2C1->CR1 &= ~I2C_CR1_ACK;
+    if(!waitStatus1()) return false;
+    if(I2C1->SR1 & I2C_SR1_AF) return false; //Must read SR1 and SR2
+    if((I2C1->SR2 & I2C_SR2_MSL)==0) return false;
+    return true;
+}
+
+bool I2C1Driver::waitStatus1()
+{
+    error=false;
+    waiting=Thread::getCurrentThread();
+    I2C1->CR2 |= I2C_CR2_ITEVTEN | I2C_CR2_ITERREN;
+    {
+        FastInterruptDisableLock dLock;
+        while(waiting)
+        {
+            waiting->IRQwait();
+            {
+                FastInterruptEnableLock eLock(dLock);
+                Thread::yield();
+            }
+        }
+    }
+    I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN);
+    return !error;
+}
+
+} //namespace miosix
diff --git a/miosix/arch/common/drivers/stm32f2_f4_i2c.h b/miosix/arch/common/drivers/stm32f2_f4_i2c.h
new file mode 100644
index 00000000..86d0ca4d
--- /dev/null
+++ b/miosix/arch/common/drivers/stm32f2_f4_i2c.h
@@ -0,0 +1,77 @@
+
+#include <interfaces/arch_registers.h>
+
+#ifndef STM32F2_I2C_H
+#define	STM32F2_I2C_H
+
+namespace miosix {
+
+/**
+ * Driver for the I2C1 peripheral in STM32F2 and STM32F4 under Miosix
+ */
+class I2C1Driver
+{
+public:
+    /**
+     * \return an instance of this class (singleton)
+     */
+    static I2C1Driver& instance();
+    
+    /**
+     * Initializes the peripheral. The only supported mode is 100KHz, master,
+     * 7bit address. Note that there is no need to manually call this member
+     * function as the constructor already inizializes the I2C peripheral.
+     * The only use of this member function is to reinitialize the peripheral
+     * if the microcontroller clock frequency or the APB prescaler is changed.
+     */
+    void init();
+    
+    /**
+     * Send data to a device connected to the I2C bus
+     * \param address device address (bit 0 is forced at 0)
+     * \param data pointer with data to send
+     * \param len length of data to send
+     * \return true on success, false on failure
+     */
+    bool send(unsigned char address, const void *data, int len);
+    
+    /**
+     * Receive data from a device connected to the I2C bus
+     * \param address device address (bit 0 is forced at 1) 
+     * \param data pointer to a buffer where data will be received
+     * \param len length of data to receive
+     * \return true on success, false on failure
+     */
+    bool recv(unsigned char address, void *data, int len);
+    
+private:
+    I2C1Driver(const I2C1Driver&);
+    I2C1Driver& operator=(const I2C1Driver&);
+    
+    /**
+     * Constructor. Initializes the peripheral except the GPIOs, that must be
+     * set by the caller to the appropriate alternate function mode prior to
+     * creating an instance of this class.
+     * \param i2c pinter to the desired I2C peripheral, such as I2C1, I2C2, ...
+     */
+    I2C1Driver() { init(); }
+    
+    /**
+     * Send a start condition
+     * \param address 
+     * \param immediateNak
+     * \return 
+     */
+    bool start(unsigned char address, bool immediateNak=false);
+    
+    /**
+     * Wait until until an interrupt occurs during the send start bit and
+     * send address phases of the i2c communication.
+     * \return true if the operation was successful, false on error
+     */
+    bool waitStatus1();
+};
+
+} //namespace miosix
+
+#endif //STM32F2_I2C_H
diff --git a/miosix/arch/cortexM3_stm32f2/stm32f205rg_sony-newman/interfaces-impl/bsp.cpp b/miosix/arch/cortexM3_stm32f2/stm32f205rg_sony-newman/interfaces-impl/bsp.cpp
index 8e1132f9..e7f07ce2 100644
--- a/miosix/arch/cortexM3_stm32f2/stm32f205rg_sony-newman/interfaces-impl/bsp.cpp
+++ b/miosix/arch/cortexM3_stm32f2/stm32f205rg_sony-newman/interfaces-impl/bsp.cpp
@@ -37,9 +37,31 @@
 #include "interfaces/portability.h"
 #include "interfaces/arch_registers.h"
 #include "config/miosix_settings.h"
+#include <algorithm>
+
+using namespace std;
 
 namespace miosix {
 
+FastMutex i2cMutex;
+
+bool i2cWriteReg(miosix::I2C1Driver& i2c, unsigned char dev, unsigned char reg,
+        unsigned char data)
+{
+    const unsigned char buffer[]={reg,data};
+    return i2c.send(dev,buffer,sizeof(buffer));
+}
+
+bool i2cReadReg(miosix::I2C1Driver& i2c, unsigned char dev, unsigned char reg,
+        unsigned char& data)
+{
+    if(i2c.send(dev,&reg,1)==false) return false;
+    unsigned char temp;
+    if(i2c.recv(dev,&temp,1)==false) return false;
+    data=temp;
+    return true;
+}
+
 //
 // Initialization
 //
@@ -54,16 +76,16 @@ void IRQbspInit()
     using namespace oled;
     OLED_nSS_Pin::mode(Mode::OUTPUT);
     OLED_nSS_Pin::high();
-    OLED_nSS_Pin::speed(Speed::_100MHz); //Without changing the default speed
+    OLED_nSS_Pin::speed(Speed::_50MHz); //Without changing the default speed
     OLED_SCK_Pin::mode(Mode::ALTERNATE); //OLED does not work!
     OLED_SCK_Pin::alternateFunction(5);
-    OLED_SCK_Pin::speed(Speed::_100MHz);
+    OLED_SCK_Pin::speed(Speed::_50MHz);
     OLED_MOSI_Pin::mode(Mode::ALTERNATE);
     OLED_MOSI_Pin::alternateFunction(5);
-    OLED_MOSI_Pin::speed(Speed::_100MHz);
+    OLED_MOSI_Pin::speed(Speed::_50MHz);
     OLED_A0_Pin::mode(Mode::OUTPUT);
     OLED_A0_Pin::low();
-    OLED_A0_Pin::speed(Speed::_100MHz);
+    OLED_A0_Pin::speed(Speed::_50MHz);
     OLED_Reset_Pin::mode(Mode::OUTPUT);
     OLED_Reset_Pin::low();
     OLED_Reset_Pin::speed(Speed::_50MHz);
@@ -166,6 +188,7 @@ void IRQbspInit()
 
 void bspInit2()
 {
+    PowerManagement::instance(); //This initializes the PMU
     BUZER_PWM_Pin::high();
     Thread::sleep(200);
     BUZER_PWM_Pin::low();
@@ -201,4 +224,193 @@ void reboot()
     miosix_private::IRQsystemReboot();
 }
 
-};//namespace miosix
+//As usual, since the PMU datasheet is unavailable (we don't even know what
+//chip it is), these are taken from underverk's code
+#define CHGSTATUS               0x01
+
+#define CH_ACTIVE_MSK           0x08
+
+#define CHGCONFIG0              0x02
+
+#define VSYS_4_4V               0x40
+#define VSYS_5V                 0x80
+#define ACIC_100mA_DPPM_ENABLE  0x00
+#define ACIC_500mA_DPPM_ENABLE  0x10
+#define ACIC_500mA_DPPM_DISABLE 0x20
+#define ACIC_USB_SUSPEND        0x20
+#define TH_LOOP                 0x08
+#define DYN_TMR                 0x04
+#define TERM_EN                 0x02
+#define CH_EN                   0x01
+
+#define CHGCONFIG1              0x03
+#define I_PRE_05                0x00
+#define I_PRE_10                0x40
+#define I_PRE_15                0x80
+#define I_PRE_20                0xC0
+
+#define DEFDCDC                 0x07
+#define DCDC1_DEFAULT           0x29
+#define DCDC_DISCH              0x40
+#define HOLD_DCDC1              0x80
+
+#define ISET_25                 0x00
+#define ISET_50                 0x10
+#define ISET_75                 0x20
+#define ISET_100                0x30
+
+#define I_TERM_05               0x00
+#define I_TERM_10               0x04
+#define I_TERM_15               0x08
+#define I_TERM_20               0x0C
+
+#define CHGCONFIG2              0x04
+#define SFTY_TMR_4h             0x0
+#define SFTY_TMR_5h             0x40
+#define SFTY_TMR_6h             0x80
+#define SFTY_TMR_8h             0xC0
+
+#define PRE_TMR_30m             0x0
+#define PRE_TMR_60m             0x20
+
+#define NTC_100k                0x0
+#define NTC_10k                 0x8
+
+#define V_DPPM_VBAT_100mV       0x0
+#define V_DPPM_4_3_V            0x04
+
+#define VBAT_COMP_ENABLE        0x02
+#define VBAT_COMP_DISABLE       0x00
+
+//
+// class PowerManagement
+//
+
+PowerManagement& PowerManagement::instance()
+{
+    static PowerManagement singleton;
+    return singleton;
+}
+
+bool PowerManagement::isUsbConnected() const
+{
+    return usb::USB5V_Detected_Pin::value();
+}
+
+bool PowerManagement::isCharging()
+{
+    Lock<FastMutex> l(i2cMutex);
+    unsigned char chgstatus;
+    //During testing the i2c command never failed. If it does, we lie and say
+    //we're not charging
+    if(i2cReadReg(i2c,PMU_I2C_ADDRESS,CHGSTATUS,chgstatus)==false) return false;
+    return (chgstatus & CH_ACTIVE_MSK)!=0;
+}
+
+int PowerManagement::getBatteryStatus()
+{
+    const int battCharged=4000; //4.0V
+    const int battDead=3000; //3V
+    return max(0,min(100,(getBatteryVoltage()-battDead)*100/(battCharged-battDead)));
+}
+
+int PowerManagement::getBatteryVoltage()
+{
+    Lock<FastMutex> l(batteryMutex);
+    power::BATT_V_ON_Pin::high(); //Enable battry measure circuitry
+    ADC1->CR2=ADC_CR2_ADON; //Turn ADC ON
+    Thread::sleep(5); //Wait for voltage to stabilize
+    ADC1->CR2 |= ADC_CR2_SWSTART; //Start conversion
+    while((ADC1->SR & ADC_SR_EOC)==0) ; //Wait for conversion
+    int result=ADC1->DR; //Read result
+    ADC1->CR2=0; //Turn ADC OFF
+    power::BATT_V_ON_Pin::low(); //Disable battery measure circuitry
+    return result*4440/4095;
+}
+
+PowerManagement::PowerManagement() : i2c(I2C1Driver::instance()),
+        chargingAllowed(true)
+{
+    {
+        FastInterruptDisableLock dLock;
+        RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
+    }
+    ADC1->CR1=0;
+    ADC1->CR2=0; //Keep the ADC OFF to save power
+    ADC1->SMPR2=ADC_SMPR2_SMP2_0; //Sample for 15 cycles channel 2 (battery)
+    ADC1->SQR1=0; //Do only one conversion
+    ADC1->SQR2=0;
+    ADC1->SQR3=2; //Convert channel 2 (battery voltage)
+    
+    unsigned char config0=VSYS_4_4V
+                        | ACIC_100mA_DPPM_ENABLE
+                        | TH_LOOP
+                        | DYN_TMR
+                        | TERM_EN
+                        | CH_EN;
+    unsigned char config1=I_TERM_10
+                        | ISET_100
+                        | I_PRE_10;
+    unsigned char config2=SFTY_TMR_5h
+                        | PRE_TMR_30m
+                        | NTC_10k
+                        | V_DPPM_4_3_V
+                        | VBAT_COMP_ENABLE;
+    unsigned char defdcdc=DCDC_DISCH
+                        | DCDC1_DEFAULT;
+    Lock<FastMutex> l(i2cMutex);
+    bool error=false;
+    if(i2cWriteReg(i2c,PMU_I2C_ADDRESS,CHGCONFIG0,config0)==false) error=true;
+    if(i2cWriteReg(i2c,PMU_I2C_ADDRESS,CHGCONFIG1,config1)==false) error=true;
+    if(i2cWriteReg(i2c,PMU_I2C_ADDRESS,CHGCONFIG2,config2)==false) error=true;
+    if(i2cWriteReg(i2c,PMU_I2C_ADDRESS,DEFDCDC,defdcdc)==false) error=true;
+    if(!error) return;
+    //Should never happen
+    for(int i=0;i<10;i++)
+    {
+        BUZER_PWM_Pin::high();
+        Thread::sleep(200);
+        BUZER_PWM_Pin::low();
+        Thread::sleep(200);
+    }
+}
+
+//
+// class LightSensor
+//
+
+LightSensor& LightSensor::instance()
+{
+    static LightSensor singleton;
+    return singleton;
+}
+
+int LightSensor::read()
+{
+    Lock<FastMutex> l(lightMutex);
+    power::ENABLE_LIGHT_SENSOR_Pin::high(); //Enable battry measure circuitry
+    ADC2->CR2=ADC_CR2_ADON; //Turn ADC ON
+    Thread::sleep(5); //Wait for voltage to stabilize
+    ADC2->CR2 |= ADC_CR2_SWSTART; //Start conversion
+    while((ADC2->SR & ADC_SR_EOC)==0) ; //Wait for conversion
+    int result=ADC2->DR; //Read result
+    ADC2->CR2=0; //Turn ADC OFF
+    power::ENABLE_LIGHT_SENSOR_Pin::low(); //Disable battery measure circuitry
+    return result;
+}
+
+LightSensor::LightSensor()
+{
+    {
+        FastInterruptDisableLock dLock;
+        RCC->APB2ENR |= RCC_APB2ENR_ADC2EN;
+    }
+    ADC2->CR1=0;
+    ADC2->CR2=0; //Keep the ADC OFF to save power
+    ADC2->SMPR1=ADC_SMPR1_SMP14_0; //Sample for 15 cycles channel 14
+    ADC2->SQR1=0; //Do only one conversion
+    ADC2->SQR2=0;
+    ADC2->SQR3=14; //Convert channel 14 (light sensor)
+}
+
+} //namespace miosix
diff --git a/miosix/arch/cortexM3_stm32f2/stm32f205rg_sony-newman/interfaces-impl/bsp_impl.h b/miosix/arch/cortexM3_stm32f2/stm32f205rg_sony-newman/interfaces-impl/bsp_impl.h
index ee06b0db..45186f31 100644
--- a/miosix/arch/cortexM3_stm32f2/stm32f205rg_sony-newman/interfaces-impl/bsp_impl.h
+++ b/miosix/arch/cortexM3_stm32f2/stm32f205rg_sony-newman/interfaces-impl/bsp_impl.h
@@ -36,6 +36,8 @@
 #include "config/miosix_settings.h"
 #include "hwmapping.h"
 #include "drivers/stm32_hardware_rng.h"
+#include "drivers/stm32f2_f4_i2c.h"
+#include "kernel/sync.h"
 
 namespace miosix {
 
@@ -43,6 +45,125 @@ namespace miosix {
 inline void ledOn()  {}
 inline void ledOff() {}
 
-}
+/**
+ * You <b>must</b> lock this mutex before accessing the I2C1Driver directly
+ * on this board, as there are multiple threads that access the I2C for
+ * different purposes (touchscreen, accelerometer, PMU). If you don't do it,
+ * your application will crash sooner or later.
+ */
+extern FastMutex i2cMutex;
+
+enum {
+     PMU_I2C_ADDRESS=0x90,  ///< I2C Address of the PMU
+     TOUCH_I2C_ADDRESS=0x0a,///< I2C Address of the touchscreen controller
+     ACCEL_I2C_ADDRESS=0x30 ///< I2C Address of the accelerometer
+};
+
+/**
+ * Helper function to write a register of an I2C device. Don't forget to lock
+ * i2cMutex before calling this.
+ * \param i2c the I2C driver
+ * \param dev device address (PMU_I2C_ADDRESS)
+ * \param reg register address
+ * \param data byte to write
+ * \return true on success, false on failure
+ */
+bool i2cWriteReg(miosix::I2C1Driver& i2c, unsigned char dev, unsigned char reg,
+        unsigned char data);
+
+/**
+ * Helper function to write a register of an I2C device. Don't forget to lock
+ * i2cMutex before calling this.
+ * \param i2c the I2C driver
+ * \param dev device address (PMU_I2C_ADDRESS)
+ * \param reg register address
+ * \param data byte to write
+ * \return true on success, false on failure
+ */
+bool i2cReadReg(miosix::I2C1Driver& i2c, unsigned char dev, unsigned char reg,
+        unsigned char& data);
+
+/**
+ * This class contains all what regards power management on the watch.
+ * The class can be safely used by multiple threads concurrently.
+ */
+class PowerManagement
+{
+public:
+    /**
+     * \return an instance of the power management class (singleton) 
+     */
+    static PowerManagement& instance();
+    
+    /**
+     * \return true if the USB cable is connected 
+     */
+    bool isUsbConnected() const;
+    
+    /**
+     * \return true if the battery is currently charging. For this to happen,
+     * charging must be allowed, the USB cable must be connected, and the
+     * battery must not be fully charged. 
+     */
+    bool isCharging();
+    
+    /** 
+     * \return the battery charge status as a number in the 0..100 range.
+     * The reading takes ~5ms 
+     */
+    int getBatteryStatus();
+    
+    /**
+     * \return the battery voltage, in millivolts. So 3700 means 3.7V. 
+     * The reading takes ~5ms 
+     */
+    int getBatteryVoltage();
+    
+private:
+    PowerManagement(const PowerManagement&);
+    PowerManagement& operator=(const PowerManagement&);
+    
+    /**
+     * Constructor
+     */
+    PowerManagement();
+    
+    I2C1Driver &i2c;
+    bool chargingAllowed;
+    FastMutex batteryMutex;
+};
+
+/**
+ * This allows to retrieve the light value
+ * The class can be safely used by multiple threads concurrently.
+ */
+class LightSensor
+{
+public:
+    /**
+     * \return an instance of the power management class (singleton) 
+     */
+    static LightSensor& instance();
+    
+    /**
+     * \return the light value. The reading takes ~5ms 
+     */
+    int read();
+    
+private:
+    LightSensor(const LightSensor&);
+    LightSensor& operator=(const LightSensor&);
+    
+    /**
+     * Constructor
+     */
+    LightSensor();
+    
+    FastMutex lightMutex;
+};
+
+
+
+} //namespace miosix
 
 #endif //BSP_IMPL_H
diff --git a/miosix/config/Makefile.inc b/miosix/config/Makefile.inc
index 910993d6..dce538c4 100644
--- a/miosix/config/Makefile.inc
+++ b/miosix/config/Makefile.inc
@@ -843,6 +843,7 @@ else ifeq ($(ARCH),cortexM3_stm32f2)
         ## Select architecture specific files
         ## These are the files in arch/<arch name>/<board name>
         ARCH_SRC :=                                  \
+        arch/common/drivers/stm32f2_f4_i2c.cpp       \
         $(BOARD_INC)/interfaces-impl/console.cpp     \
         $(BOARD_INC)/interfaces-impl/delays.cpp      \
         $(BOARD_INC)/interfaces-impl/bsp.cpp
diff --git a/miosix_np_2/nbproject/configurations.xml b/miosix_np_2/nbproject/configurations.xml
index 1d41c781..31ed01df 100644
--- a/miosix_np_2/nbproject/configurations.xml
+++ b/miosix_np_2/nbproject/configurations.xml
@@ -42,6 +42,8 @@
               <in>dcc.h</in>
               <in>stm32_hardware_rng.cpp</in>
               <in>stm32_hardware_rng.h</in>
+              <in>stm32f2_f4_i2c.cpp</in>
+              <in>stm32f2_f4_i2c.h</in>
             </df>
           </df>
           <df name="cortexM3_stm32">
@@ -157,6 +159,9 @@
               <in>arch_settings.h</in>
             </df>
             <df name="stm32f205rg_sony-newman">
+              <df name="core">
+                <in>stage_1_boot.cpp</in>
+              </df>
               <df name="interfaces-impl">
                 <in>bsp.cpp</in>
                 <in>bsp_impl.h</in>
@@ -8097,6 +8102,16 @@
             tool="3"
             flavor2="0">
       </item>
+      <item path="../miosix/arch/common/drivers/stm32f2_f4_i2c.cpp"
+            ex="false"
+            tool="1"
+            flavor2="0">
+      </item>
+      <item path="../miosix/arch/common/drivers/stm32f2_f4_i2c.h"
+            ex="false"
+            tool="3"
+            flavor2="0">
+      </item>
       <item path="../miosix/arch/cortexM3_stm32/common/CMSIS/core_cm3.c"
             ex="false"
             tool="0"
@@ -14869,6 +14884,11 @@
             tool="3"
             flavor2="0">
       </item>
+      <item path="../miosix/arch/cortexM3_stm32f2/stm32f205rg_sony-newman/core/stage_1_boot.cpp"
+            ex="false"
+            tool="1"
+            flavor2="0">
+      </item>
       <item path="../miosix/arch/cortexM3_stm32f2/stm32f205rg_sony-newman/interfaces-impl/bsp.cpp"
             ex="false"
             tool="1"
diff --git a/miosix_np_2/nbproject/private/configurations.xml b/miosix_np_2/nbproject/private/configurations.xml
index 3701528d..3f209690 100644
--- a/miosix_np_2/nbproject/private/configurations.xml
+++ b/miosix_np_2/nbproject/private/configurations.xml
@@ -42,6 +42,8 @@
               <in>dcc.h</in>
               <in>stm32_hardware_rng.cpp</in>
               <in>stm32_hardware_rng.h</in>
+              <in>stm32f2_f4_i2c.cpp</in>
+              <in>stm32f2_f4_i2c.h</in>
             </df>
           </df>
           <df name="cortexM3_stm32">
diff --git a/miosix_np_2/nbproject/private/private.xml b/miosix_np_2/nbproject/private/private.xml
index 53489479..f1b9e16b 100644
--- a/miosix_np_2/nbproject/private/private.xml
+++ b/miosix_np_2/nbproject/private/private.xml
@@ -5,7 +5,7 @@
     </code-assistance-data>
     <data xmlns="http://www.netbeans.org/ns/make-project-private/1">
         <activeConfTypeElem>0</activeConfTypeElem>
-        <activeConfIndexElem>2</activeConfIndexElem>
+        <activeConfIndexElem>13</activeConfIndexElem>
     </data>
     <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
     <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/>
-- 
GitLab