This is the code used on my video series "Hybrid Supercapacitor Car Battery" for my own hardware monitoring system. THe videos can be found on madelectronengineering.com
Dependencies: BurstSPI Fonts INA219 mbed LPC1114_WakeInterruptIn
Fork of SharpMemoryLCD by
Revision 2:0c49a8f32f6e, committed 2017-12-26
- Comitter:
- madelectroneng
- Date:
- Tue Dec 26 21:30:09 2017 +0000
- Parent:
- 1:1bd2b42b305d
- Commit message:
- Inital setup
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Device/LPC1114_WakeInterruptIn.lib Tue Dec 26 21:30:09 2017 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/Sissors/code/LPC1114_WakeInterruptIn/#128f3fe79240
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Device/WakeUp_Freescale.cpp Tue Dec 26 21:30:09 2017 +0000 @@ -0,0 +1,135 @@ +#if defined(TARGET_Freescale) + +#include "WakeUp.h" +#include "us_ticker_api.h" + +Callback<void()> WakeUp::callback; +float WakeUp::cycles_per_ms = 1.0; + +static uint16_t remainder_count; +static uint32_t oldvector; +static uint8_t oldPSR; + +//See if we have a 32kHz crystal on the clock input +//Check if the DMX32 bit is set, not perfect, but most cases will work +static inline bool is32kXtal(void) { + return (MCG->C4 & MCG_C4_DMX32_MASK); +} + +void restore(void); + +void WakeUp::set_ms(uint32_t ms) +{ + /* Clock the timer */ + SIM->SCGC5 |= 0x1u; + + //Check if it is running, in that case, store current values + remainder_count = 0; + if (NVIC_GetVector(LPTimer_IRQn) != (uint32_t)WakeUp::irq_handler) { + oldvector = NVIC_GetVector(LPTimer_IRQn); + oldPSR = LPTMR0->PSR; + + if (LPTMR0->CSR & LPTMR_CSR_TIE_MASK) { + //Write first to sync value + LPTMR0->CNR = 0; + uint16_t countval = LPTMR0->CNR; + if (countval < LPTMR0->CMR) + remainder_count = countval - LPTMR0->CMR; + } + } + + LPTMR0->CSR = 0; + + if (ms != 0) { + /* Set interrupt handler */ + NVIC_SetVector(LPTimer_IRQn, (uint32_t)WakeUp::irq_handler); + NVIC_EnableIRQ(LPTimer_IRQn); + + uint32_t counts; + //Set clock + if (is32kXtal()) { + SIM->SOPT1 &= ~SIM_SOPT1_OSC32KSEL_MASK; //Put RTC/LPTMR on 32kHz external. + #ifdef OSC0 + OSC0->CR |= OSC_CR_EREFSTEN_MASK; + #else + OSC->CR |= OSC_CR_EREFSTEN_MASK; + #endif + LPTMR0->PSR = LPTMR_PSR_PCS(2); + counts = (uint32_t)((float)ms * 32.768f); + } else { + //Clock from the 1kHz LPO + LPTMR0->PSR = LPTMR_PSR_PCS(1); + counts = (uint32_t)((float)ms * cycles_per_ms); + } + + //If no prescaler is needed + if (counts <= 0xFFFF) + LPTMR0->PSR |= LPTMR_PSR_PBYP_MASK; + else { //Otherwise increase prescaler until it fits + counts >>= 1; + uint32_t prescaler = 0; + while (counts > 0xFFFF) { + counts >>= 1; + prescaler++; + } + LPTMR0->PSR |= LPTMR_PSR_PRESCALE(prescaler); + } + LPTMR0->CMR = counts; + + LPTMR0->CSR = LPTMR_CSR_TIE_MASK; + LPTMR0->CSR |= LPTMR_CSR_TEN_MASK; + } else { + restore(); + } + +} + + +void WakeUp::irq_handler(void) +{ + // write 1 to TCF to clear the LPT timer compare flag + LPTMR0->CSR |= LPTMR_CSR_TCF_MASK; + restore(); + callback.call(); +} + +void WakeUp::calibrate(void) +{ + if (!is32kXtal()) { + wait_us(1); //Otherwise next wait might overwrite our settings + cycles_per_ms = 1.0; + set_ms(1100); + wait_ms(100); + + //Write first to sync value + LPTMR0->CNR = 0; + uint32_t ticks = LPTMR0->CNR; + cycles_per_ms = ticks / 100.0; + set_ms(0); + } +} + +void restore(void){ + /* Reset */ + LPTMR0->CSR = 0; + + /* Set interrupt handler */ + NVIC_SetVector(LPTimer_IRQn, oldvector); + NVIC_EnableIRQ(LPTimer_IRQn); + + /* Clock at (1)MHz -> (1)tick/us */ + LPTMR0->PSR = oldPSR; + + if (remainder_count) { + /* Set the compare register */ + LPTMR0->CMR = remainder_count; + + /* Enable interrupt */ + LPTMR0->CSR |= LPTMR_CSR_TIE_MASK; + + /* Start the timer */ + LPTMR0->CSR |= LPTMR_CSR_TEN_MASK; + } +} + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Device/WakeUp_LPC11XX.cpp Tue Dec 26 21:30:09 2017 +0000 @@ -0,0 +1,165 @@ +/** +See homepage of this lib for LPC11xx special treatment +**/ + +#ifdef TARGET_LPC11XX_11CXX + +#include "WakeUp.h" +#include "WakeInterruptIn.h" +#include "pinmap.h" +#include "toolchain.h" + +//Pin used, allowed pins = P0_1 (dp24, default), P0_8 (dp1) and P0_9 (dp2) +//By defining WakeUpPin in for example your main.cpp this can be overridden +WEAK PinName WakeUpPin = dp24; +extern PinName WakeUpPin; + +WakeInterruptIn IRQ_in(WakeUpPin); +PwmOut pulse_out(WakeUpPin); + +Callback<void()> WakeUp::callback; +float WakeUp::cycles_per_ms = 20.0; + +static uint32_t old_clk_sel = ~0; +static uint32_t SYSAHBCLKCTRL; +static uint32_t TCR, PR, MR3; +static LPC_TMR_TypeDef *WakeUpTimer; +static uint32_t SYSAHBCLKCTRL_Sleep; +static uint8_t WakeUpTimer_Match; + +static inline void restore(void); + + +void WakeUp::set_ms(uint32_t ms) +{ + if (old_clk_sel == ~0) { //Only during first run + old_clk_sel = LPC_SYSCON->MAINCLKSEL; + SYSAHBCLKCTRL = LPC_SYSCON->SYSAHBCLKCTRL; + + switch(WakeUpPin) { + case dp24: + WakeUpTimer = LPC_TMR32B0; + SYSAHBCLKCTRL_Sleep = 0x15 | (1<<9); + WakeUpTimer_Match = 2; + break; + case dp1: + WakeUpTimer = LPC_TMR16B0; + SYSAHBCLKCTRL_Sleep = 0x15 | (1<<7); + WakeUpTimer_Match = 0; + break; + case dp2: + WakeUpTimer = LPC_TMR16B0; + SYSAHBCLKCTRL_Sleep = 0x15 | (1<<7); + WakeUpTimer_Match = 1; + break; + default: + error("Invalid WakeUp pin, choose dp1, dp2 or dp24"); + } + } + + if (ms != 0) { + if (LPC_SYSCON->SYSAHBCLKCTRL != SYSAHBCLKCTRL_Sleep) //Always when it is different from sleep settings + SYSAHBCLKCTRL = LPC_SYSCON->SYSAHBCLKCTRL; + + LPC_SYSCON->PDRUNCFG &= ~PDRUNCFG_WDTOSC_PD; + LPC_SYSCON->PDSLEEPCFG = 0x000018B7 | (LPC_SYSCON->PDRUNCFG & (PDRUNCFG_WDTOSC_PD | PDRUNCFG_BOD_PD)); + + //Set oscillator for 20kHz + LPC_SYSCON->WDTOSCCTRL = 14 | (1<<5); + + //Store old PWM + TCR = WakeUpTimer->TCR; + PR = WakeUpTimer->PR; + MR3 = WakeUpTimer->MR3; + + //Setup PWM + WakeUpTimer->TCR = TMR16B0TCR_CRST; + uint32_t ticks = (float)ms * cycles_per_ms; + + //whatever timer it is, we treat it as 16-bit (with PR that is 32-bit still, do the math, it is enough for this) + WakeUpTimer->PR = ticks >> 16; + WakeUpTimer->MR[WakeUpTimer_Match] = ticks / ((ticks >> 16) + 1); + WakeUpTimer->MR3 = 0xFFFF; + + IRQ_in.rise(irq_handler); + + //Disable most peripherals + LPC_SYSCON->SYSAHBCLKCTRL = SYSAHBCLKCTRL_Sleep; + + //Switch clock to WD OSC + LPC_SYSCON->MAINCLKSEL = 0x2; + LPC_SYSCON->MAINCLKUEN = 0; + LPC_SYSCON->MAINCLKUEN = MAINCLKUEN_ENA; + + //Enable PWM: + WakeUpTimer->TCR = TMR16B0TCR_CEN; + } else { + //Else restore normal settings + restore(); + } + +} + +void WakeUp::irq_handler(void) +{ + restore(); + callback.call(); +} + +void WakeUp::calibrate(void) +{ + //Save current pin function + __IO uint32_t *reg = (__IO uint32_t*)(LPC_IOCON_BASE + (dp24 & 0xFF)); + uint32_t old_pinfun = *reg; + + //Set oscillator for 20kHz + LPC_SYSCON->PDRUNCFG &= ~PDRUNCFG_WDTOSC_PD; + LPC_SYSCON->WDTOSCCTRL = 14 | (1<<5); + + //Direct WDT to the CLKOUT pin (dp24), sample it back + DigitalIn din(dp24); + Timer timer; + + LPC_SYSCON->CLKOUTDIV = 1; + LPC_SYSCON->CLKOUTCLKSEL = 0x2; + LPC_SYSCON->CLKOUTUEN = 0; + LPC_SYSCON->CLKOUTUEN = CLKOUTUEN_ENA; + pin_function(dp24, 1); + + int count = 0; + timer.start(); + while (timer.read_ms() < 100) { + while (din.read() == 0); + while (din.read() == 1); + count++; + } + cycles_per_ms = (float)count / 100.0f; + + //Set old pin function back, disable CLKOUT + *reg = old_pinfun; + LPC_SYSCON->CLKOUTDIV = 0; +} + +static inline void restore(void) { + + WakeUpTimer->MR[WakeUpTimer_Match] = 0xFFFFFFFF; + + if (old_clk_sel == 3) //Was running on PLL + while(LPC_SYSCON->SYSPLLSTAT != SYSPLLSTAT_LOCK); + + if (old_clk_sel < 4) { //If valid setting + LPC_SYSCON->MAINCLKSEL = old_clk_sel; + LPC_SYSCON->MAINCLKUEN = 0; + LPC_SYSCON->MAINCLKUEN = MAINCLKUEN_ENA; + } + + IRQ_in.rise(NULL); + + LPC_SYSCON->SYSAHBCLKCTRL = SYSAHBCLKCTRL; + + WakeUpTimer->MR3 = MR3; + WakeUpTimer->PR = PR; + WakeUpTimer->TCR = TCR; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Device/WakeUp_LPC11u24.cpp Tue Dec 26 21:30:09 2017 +0000 @@ -0,0 +1,63 @@ +/** +Due to lack of another option for the LPC11u24 the watchdog timer is used as wakeup source. +Since if the reset on watchdog event bit is set, I cannot remove it again, this means if you also got watchdog code running +the most likely result is that it just resets your board. +**/ + + +#ifdef TARGET_LPC11U24 + +#include "WakeUp.h" + +Callback<void()> WakeUp::callback; +float WakeUp::cycles_per_ms = 5.0; + +void WakeUp::set_ms(uint32_t ms) +{ + if (ms != 0) { + LPC_SYSCON->SYSAHBCLKCTRL |= 0x8000; + LPC_SYSCON->PDRUNCFG &= ~(1<<6); + LPC_SYSCON->PDSLEEPCFG &= ~(1<<6); + LPC_SYSCON->STARTERP1 |= 1<<12; + + //Set oscillator for 20kHz = 5kHz after divide by 4 in WDT + LPC_SYSCON->WDTOSCCTRL = 14 | (1<<5); + + LPC_WWDT->MOD = 1; //Enable WDT + LPC_WWDT->TC = (uint32_t)((float)ms * cycles_per_ms); + LPC_WWDT->CLKSEL = 1; //WDTOSC + LPC_WWDT->WARNINT = 0; + + NVIC_SetVector(WDT_IRQn, (uint32_t)WakeUp::irq_handler); + + //Feeeeeed me + LPC_WWDT->FEED = 0xAA; + LPC_WWDT->FEED = 0x55; + + NVIC_EnableIRQ(WDT_IRQn); + } else + NVIC_DisableIRQ(WDT_IRQn); + +} + +void WakeUp::irq_handler(void) +{ + LPC_WWDT->MOD = 1<<3; + callback.call(); +} + +void WakeUp::calibrate(void) +{ + cycles_per_ms = 5.0; + set_ms(1100); + wait_ms(10); //Give time to sync + uint32_t count1 = LPC_WWDT->TV; + wait_ms(100); + uint32_t count2 = LPC_WWDT->TV; + set_ms(0); + count1 = count1 - count2; + + cycles_per_ms = count1 / 100.0; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Device/WakeUp_LPC812.cpp Tue Dec 26 21:30:09 2017 +0000 @@ -0,0 +1,74 @@ +#ifdef TARGET_LPC812 + +#include "WakeUp.h" + +Callback<void()> WakeUp::callback; +float WakeUp::cycles_per_ms = 10.0; + +void WakeUp::set_ms(uint32_t ms) +{ + //Enable clock to register interface: + LPC_SYSCON->SYSAHBCLKCTRL |= 1<<9; + + //Clear the counter: + LPC_WKT->CTRL |= 1<<2; + if (ms != 0) { + //Enable clock to register interface: + LPC_SYSCON->SYSAHBCLKCTRL |= 1<<9; + + //Set 10kHz timer as source, and just to be sure clear status bit + LPC_WKT->CTRL = 3; + + //Enable the 10kHz timer + LPC_PMU->DPDCTRL |= (1<<2) | (1<<3); + + //Set interrupts + NVIC_SetVector(WKT_IRQn, (uint32_t)WakeUp::irq_handler); + NVIC_EnableIRQ(WKT_IRQn); + + //Load the timer + LPC_WKT->COUNT = (uint32_t)((float)ms * cycles_per_ms); + + } else { + //Disable clock to register interface: + LPC_SYSCON->SYSAHBCLKCTRL &= ~(1<<9); + + //Disable the 10kHz timer + LPC_PMU->DPDCTRL &= ~((1<<2) | (1<<3)); + } +} + +void WakeUp::irq_handler(void) +{ + //Clear status + LPC_WKT->CTRL |= 2; + + //Disable clock to register interface: + LPC_SYSCON->SYSAHBCLKCTRL &= ~(1<<9); + + //Disable the 10kHz timer + LPC_PMU->DPDCTRL &= ~((1<<2) | (1<<3)); + + callback.call(); +} + +void WakeUp::calibrate(void) +{ + cycles_per_ms = 10.0; + set_ms(1100); + wait_ms(100); + + uint32_t prevread = LPC_WKT->COUNT; + uint32_t read = LPC_WKT->COUNT; + while( read != prevread) { + prevread = read; + read = LPC_WKT->COUNT; + } + + uint32_t ticks = 11000 - read; + + cycles_per_ms = ticks / 100.0; + set_ms(0); +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Device/WakeUp_STM32_others.cpp Tue Dec 26 21:30:09 2017 +0000 @@ -0,0 +1,188 @@ +// Created new file only for STM32/Nucleo boards which I have own it +// by JH1PJL 2017-9-21 + +#if defined(TARGET_NUCLEO_F446RE) || defined(TARGET_NUCLEO_F411RE)\ + || defined(TARGET_NUCLEO_F401RE)\ + || defined(TARGET_NUCLEO_L152RE) || defined(TARGET_NUCLEO_L073RZ)\ + || defined(TARGET_NUCLEO_L053R8) + +#include "WakeUp.h" +#include "rtc_api.h" + +//#define DEBUG + +extern Serial pc; + +#if 0 +#define DBG(...) pc.printf(__VA_ARGS__) +#else +#define DBG(...) {;} +#endif + +#if 0 +#define DBGP(...) pc.printf(__VA_ARGS__) +#else +#define DBGP(...) {;} +#endif + +#define BYTE2BCD(byte) ((byte % 10) | ((byte / 10) << 4)) + +//Most things are pretty similar between the different STM targets. +//Only the IRQ number the alarm is connected to differs. Any errors +//with RTC_IRQn/RTC_Alarm_IRQn in them are related to this +#if defined(TARGET_M4) || defined(TARGET_M3) +#define RTC_IRQ RTC_Alarm_IRQn +#else +#define RTC_IRQ RTC_IRQn +#endif + +// Some things to handle Disco L476VG (and similar ones) +#if defined(TARGET_STM32L4) +#define IMR IMR1 +#define EMR EMR1 +#define RTSR RTSR1 +#define FTSR FTSR2 +#define PR PR1 +#endif + +Callback<void()> WakeUp::callback; +bool WakeUp::use_reset = false; + + +void WakeUp::set_ms(uint32_t ms) +{ + if (ms == 0) { //Just disable alarm + return; + } + + if (!rtc_isenabled()) { //Make sure RTC is running + rtc_init(); + wait_us(250); //The f401 seems to want a delay after init + } + + PWR->CR |= PWR_CR_DBP; //Enable power domain + RTC->WPR = 0xCA; //Disable RTC write protection + RTC->WPR = 0x53; + + //Alarm must be disabled to change anything + RTC->CR &= ~RTC_CR_ALRAE; + RTC->CR &= 0x00ff00ff; + while(!(RTC->ISR & RTC_ISR_ALRAWF)); + + DBG("Step(%u)\r\n", __LINE__); + + //RTC prescaler + calculate how many sub-seconds should be added + uint32_t prescaler = (RTC->PRER & 0x7FFF) + 1; + uint32_t subsecsadd = ((ms % 1000) * prescaler) / 1000; + + if ((ms < 1000) && (subsecsadd < 2)) + subsecsadd = 2;//At least 5 subsecs delay to be sure interrupt is called + + __disable_irq(); //At this point we don't want IRQs anymore + + //Get current time + uint32_t subsecs = RTC->SSR; + time_t secs = rtc_read(); + DBG("Step(%u),secs:%d,subsecs:%d\r\n", __LINE__, secs, subsecs); + + //Calculate alarm values + //Subseconds is countdown, + // so substract the 'added' sub-seconds and prevent underflow + if (subsecs < subsecsadd) { + subsecs += prescaler; + secs++; + } + subsecs -= subsecsadd; + + //Set seconds correctly + secs += ms / 1000; + struct tm *timeinfo = localtime(&secs); + DBG("Step(%u),secs:%d\r\n", __LINE__, secs); + + //Enable rising edge EXTI interrupt of the RTC + EXTI->IMR |= RTC_EXTI_LINE_ALARM_EVENT; // enable it + EXTI->EMR &= ~RTC_EXTI_LINE_ALARM_EVENT; // disable event + EXTI->RTSR |= RTC_EXTI_LINE_ALARM_EVENT; // enable rising edge + EXTI->FTSR &= ~RTC_EXTI_LINE_ALARM_EVENT; // disable falling edge + + //Calculate alarm register values + uint32_t alarmreg = 0; + alarmreg |= BYTE2BCD(timeinfo->tm_sec) << 0; + alarmreg |= BYTE2BCD(timeinfo->tm_min) << 8; + alarmreg |= BYTE2BCD(timeinfo->tm_hour) << 16; + alarmreg |= BYTE2BCD(timeinfo->tm_mday) << 24; + alarmreg &= 0x3f3f7f7f; // All MSKx & WDSEL = 0 + + //Enable RTC interrupt (use Alarm-A) + DBG("Step(%u),alarmreg:0x%08x\r\n", __LINE__, alarmreg); + DBG("Step(%u),RTC->ISR:0x%08x\r\n", __LINE__, RTC->ISR); + RTC->ALRMAR = alarmreg; + RTC->ALRMASSR = subsecs | RTC_ALRMASSR_MASKSS; //Mask no subseconds + DBG("Step(%u),alarmreg(reg):0x%08x\r\n", __LINE__, RTC->ALRMAR); + RTC->CR |= RTC_CR_ALRAE | RTC_CR_ALRAIE; //Enable Alarm-A + DBG("Step(%u),RTC->CR:0x%08x\r\n", __LINE__, RTC->CR); + + RTC->WPR = 0xFF; //Enable RTC write protection + PWR->CR &= ~PWR_CR_DBP; //Disable power domain + + __enable_irq(); //Alarm is set, so irqs can be enabled again + + //Enable everything else + NVIC_SetVector(RTC_IRQ, (uint32_t)WakeUp::irq_handler); + NVIC_EnableIRQ(RTC_IRQ); + use_reset = false; + //---- Only for Debug purpose + DBGP("PWR->CR 0x%08x:0x%08x\r\n",&PWR->CR, PWR->CR); + DBGP("PWR->CSR 0x%08x:0x%08x\r\n",&PWR->CSR, PWR->CSR); + DBGP("SCB->SCR 0x%08x:0x%08x\r\n",&SCB->SCR, SCB->SCR); + DBGP("SCB->AIRCR 0x%08x:0x%08x\r\n",&SCB->AIRCR, SCB->AIRCR); + DBGP("EXTI->IMR 0x%08x:0x%08x\r\n",&EXTI->IMR, EXTI->IMR); + DBGP("EXTI->EMR 0x%08x:0x%08x\r\n",&EXTI->EMR, EXTI->EMR); + DBGP("EXTI->RTSR 0x%08x:0x%08x\r\n",&EXTI->RTSR, EXTI->RTSR); + DBGP("EXTI->FTSR 0x%08x:0x%08x\r\n",&EXTI->FTSR, EXTI->FTSR); + DBGP("RTC->TR 0x%08x:0x%08x\r\n",&RTC->TR,RTC->TR); + DBGP("RTC->DR 0x%08x:0x%08x\r\n",&RTC->DR,RTC->DR); + DBGP("RTC->CR 0x%08x:0x%08x\r\n",&RTC->CR,RTC->CR); + DBGP("RTC->ISR 0x%08x:0x%08x\r\n",&RTC->ISR,RTC->ISR); + DBGP("RTC->ALRMAR 0x%08x:0x%08x\r\n",&RTC->ALRMAR,RTC->ALRMAR); +} + +void WakeUp::standby_then_reset(uint32_t ms) +{ + DBG("Step(%u),ms:%d\r\n", __LINE__, ms); + if (ms == 0){ // just go to Reset + __NVIC_SystemReset(); + } + set_ms(ms); + use_reset = true; + PWR->CR |= PWR_CR_CWUF; + HAL_PWR_EnterSTANDBYMode(); +} + +void WakeUp::irq_handler(void) +{ + //Clear RTC + EXTI interrupt flags + PWR->CR |= PWR_CR_DBP; // Enable power domain + RTC->ISR &= ~RTC_ISR_ALRAF; + RTC->CR &= 0x00ff00ff; // just in case + RTC->WPR = 0xCA; // Disable RTC write protection + RTC->WPR = 0x53; + RTC->CR &= ~(RTC_CR_ALRAE | RTC_CR_ALRAIE); //DisEnable Alarm-A + RTC->WPR = 0xFF; // Enable RTC write protection + EXTI->PR = RTC_EXTI_LINE_ALARM_EVENT; + PWR->CR &= ~PWR_CR_DBP; // Disable power domain + if (use_reset == true){ + NVIC_SystemReset(); + } else { + if (callback){ + callback.call(); + } + } +} + +void WakeUp::calibrate(void) +{ + //RTC, we assume it is accurate enough without calibration +} + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Device/WakeUp_STM_RTC.cpp Tue Dec 26 21:30:09 2017 +0000 @@ -0,0 +1,133 @@ +#ifdef TARGET_STM +// added by JH1PJL 2017-9-21 +#if defined(TARGET_NUCLEO_F446RE) || defined(TARGET_NUCLEO_F411RE)\ + || defined(TARGET_NUCLEO_F401RE)\ + || defined(TARGET_NUCLEO_L152RE) || defined(TARGET_NUCLEO_L073RZ)\ + || defined(TARGET_NUCLEO_L053R8) + // use WakeUp_STM32_others.cpp +#else + +#include "WakeUp.h" +#include "rtc_api.h" + +#define BYTE2BCD(byte) ((byte % 10) | ((byte / 10) << 4)) + +//Most things are pretty similar between the different STM targets. +//Only the IRQ number the alarm is connected to differs. Any errors +//with RTC_IRQn/RTC_Alarm_IRQn in them are related to this +#if defined(TARGET_M4) || defined(TARGET_M3) +#define RTC_IRQ RTC_Alarm_IRQn +#else +#define RTC_IRQ RTC_IRQn +#endif + +// Some things to handle Disco L476VG (and similar ones) +#if defined(TARGET_STM32L4) +#define IMR IMR1 +#define EMR EMR1 +#define RTSR RTSR1 +#define FTSR FTSR2 +#define PR PR1 +#endif + +//Disabling the Backup Powerdomain does not seem to work nicely anymore if you want to use other RTC functions afterwards. +//For now I have disabled it in code, if you find WakeUp increases your powerconsumption, try enabling it again (code is still there, just commented) + +Callback<void()> WakeUp::callback; + +void WakeUp::set_ms(uint32_t ms) +{ + if (!rtc_isenabled()) { //Make sure RTC is running + rtc_init(); + wait_us(250); //The f401 seems to want a delay after init + } + + //PWR->CR |= PWR_CR_DBP; //Enable power domain + RTC->WPR = 0xCA; //Disable RTC write protection + RTC->WPR = 0x53; + + //Alarm must be disabled to change anything + RTC->CR &= ~RTC_CR_ALRAE; + while(!(RTC->ISR & RTC_ISR_ALRAWF)); + + if (ms == 0) { //Just disable alarm + //PWR->CR &= ~PWR_CR_DBP; //Disable power domain + RTC->WPR = 0xFF; //Enable RTC write protection + return; + } + + //RTC prescaler + calculate how many sub-seconds should be added + uint32_t prescaler = (RTC->PRER & 0x7FFF) + 1; + uint32_t subsecsadd = ((ms % 1000) * prescaler) / 1000; + + if ((ms < 1000) && (subsecsadd < 2)) + subsecsadd = 2; //At least 2 subsecs delay to be sure interrupt is called + + __disable_irq(); //At this point we don't want IRQs anymore + + //Get current time + uint32_t subsecs = RTC->SSR; + time_t secs = rtc_read(); + + //Calculate alarm values + //Subseconds is countdown, so substract the 'added' sub-seconds and prevent underflow + if (subsecs < subsecsadd) { + subsecs += prescaler; + secs++; + } + subsecs -= subsecsadd; + + //Set seconds correctly + secs += ms / 1000; + struct tm *timeinfo = localtime(&secs); + + //Enable rising edge EXTI interrupt of the RTC + EXTI->IMR |= RTC_EXTI_LINE_ALARM_EVENT; + EXTI->EMR &= ~RTC_EXTI_LINE_ALARM_EVENT; + EXTI->RTSR |= RTC_EXTI_LINE_ALARM_EVENT; + EXTI->FTSR &= ~RTC_EXTI_LINE_ALARM_EVENT; + + //Calculate alarm register values + uint32_t alarmreg = 0; + alarmreg |= BYTE2BCD(timeinfo->tm_sec) << 0; + alarmreg |= BYTE2BCD(timeinfo->tm_min) << 8; + alarmreg |= BYTE2BCD(timeinfo->tm_hour) << 16; + alarmreg |= BYTE2BCD(timeinfo->tm_mday) << 24; + + //Enable RTC interrupt + RTC->ALRMAR = alarmreg; + RTC->ALRMASSR = subsecs | RTC_ALRMASSR_MASKSS; //Mask no subseconds + RTC->CR |= RTC_CR_ALRAE | RTC_CR_ALRAIE; //Enable Alarm + + RTC->WPR = 0xFF; //Enable RTC write protection + //PWR->CR &= ~PWR_CR_DBP; //Disable power domain + + __enable_irq(); //Alarm is set, so irqs can be enabled again + + //Enable everything else + NVIC_SetVector(RTC_IRQ, (uint32_t)WakeUp::irq_handler); + NVIC_EnableIRQ(RTC_IRQ); +} + + +void WakeUp::irq_handler(void) +{ + //Clear RTC + EXTI interrupt flags + //PWR->CR |= PWR_CR_DBP; //Enable power domain + RTC->ISR &= ~RTC_ISR_ALRAF; + RTC->WPR = 0xCA; //Disable RTC write protection + RTC->WPR = 0x53; + RTC->CR &= ~RTC_CR_ALRAE; + RTC->WPR = 0xFF; //Enable RTC write protection + EXTI->PR = RTC_EXTI_LINE_ALARM_EVENT; + //PWR->CR &= ~PWR_CR_DBP; //Disable power domain + callback.call(); +} + +void WakeUp::calibrate(void) +{ + //RTC, we assume it is accurate enough without calibration +} + +#endif // TARGET_NUCLEO_F446RE (added by JH1PJL 2017-9-21) +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/INA219.lib Tue Dec 26 21:30:09 2017 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/madelectroneng/code/INA219/#8ccc8e47e3d5
--- a/Images.lib Tue Aug 25 21:15:06 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://developer.mbed.org/users/star297/code/Images/#cd5f51ea83f1
--- a/SharpLCD.cpp Tue Aug 25 21:15:06 2015 +0000 +++ b/SharpLCD.cpp Tue Dec 26 21:30:09 2017 +0000 @@ -112,7 +112,7 @@ : spi(mosi, miso, sclk), chipSelect(chipSelect), Enable(enable), ExtCom(extcom) { //Initialize - spi.frequency(3500000);// Nominal speed is 1MHz, tested working at 3MHz + spi.frequency(1000000);// Nominal speed is 1MHz, tested working at 3MHz lcdPolarity = 0; rowCount = 0; memset((uint8_t*)pixelBuffer, White, sizeof(pixelBuffer)); // init full pixel buffer to White @@ -141,6 +141,12 @@ Enable = 0; } +void SharpLCD::clearspace() { + // set pixel buffer to zero but do not blank screen + memset((void*)pixelBuffer, foreground, sizeof(pixelBuffer)); + memset((void*)RowState, 0, sizeof(RowState)); + } + void SharpLCD::clearImmediate() { // Clear out the pixel buffer memset((void*)pixelBuffer, foreground, sizeof(pixelBuffer));
--- a/SharpLCD.h Tue Aug 25 21:15:06 2015 +0000 +++ b/SharpLCD.h Tue Dec 26 21:30:09 2017 +0000 @@ -12,9 +12,9 @@ /// Set the display geometry depending on the resolution of the display used /// common types are 96x96, 128x128, 400x240 /** MemoryLCD width in pixels */ -#define DISPLAY_WIDTH (128) +#define DISPLAY_WIDTH (400) /** MemoryLCD height in pixels */ -#define DISPLAY_HEIGHT (128) +#define DISPLAY_HEIGHT (240) /** Maximum length of a printf to the display */ #define MAX_PRINTF_CHARS 40 @@ -224,6 +224,12 @@ * Transfers pixel buffer to the display */ void update(); + + /** + * set pixel buffer to zero but do not blank screen. + */ + void clearspace(); + /** * Clear the display & set pixel buffer to zero. */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WakeUp.h Tue Dec 26 21:30:09 2017 +0000 @@ -0,0 +1,114 @@ +#include "mbed.h" + +/** + * Class to make wake up a microcontroller from deepsleep using a low-power timer. + * + * @code + * // Depending on the LED connections either the LED is off the 2 seconds + * // the target spends in deepsleep(), and on for the other second. Or it is inverted + * + * #include "mbed.h" + * #include "WakeUp.h" + * + * DigitalOut myled(LED1); + * + * int main() { + * wait(5); + * + * //The low-power oscillator can be quite inaccurate on some targets + * //this function calibrates it against the main clock + * WakeUp::calibrate(); + * + * while(1) { + * //Set LED to zero + * myled = 0; + * + * //Set wakeup time for 2 seconds + * WakeUp::set_ms(2000); + * + * //Enter deepsleep, the program won't go beyond this point until it is woken up + * deepsleep(); + * + * //Set LED for 1 second to one + * myled = 1; + * wait(1); + * } + * } + * @endcode + */ +class WakeUp +{ +public: + /** + * Set the timeout + * + * @param s required time in seconds + */ + static void set(uint32_t s) { + set_ms(1000 * s); + } + + /** + * Set the timeout + * + * @param ms required time in milliseconds + */ + static void set_ms(uint32_t ms); + + /** + * Attach a function to be called after timeout + * + * This is optional, if you just want to wake up you + * do not need to attach a function. + * + * Important: Many targets will run the wake-up routine + * at reduced clock speed, afterwards clock speed is restored. + * This means that clock speed dependent functions, such as printf + * might end up distorted. + * + * @code + * // Attaching regular function + * WakeUp::attach(&yourFunc); + * // Attaching member function inside another library + * WakeUp::attach(callback(this, &YourLib::yourLibFunction)); + * @endcode + * + * It uses the new Callback system to attach functions. + * + * @param *function function to call + */ + static void attach(Callback<void()> function) { + callback = function; + } + + /** + * Calibrate the timer + * + * Some of the low-power timers have very bad accuracy. + * This function calibrates it against the main timer. + * + * Warning: Blocks for 100ms! + */ + static void calibrate(void); + + /** + * Enter Standby mode then Reset + * (available only for confirmed Nucleo boards) + * + * @param ms required time in milliseconds + */ +#if defined(TARGET_NUCLEO_F446RE) || defined(TARGET_NUCLEO_F411RE)\ + || defined(TARGET_NUCLEO_F401RE)\ + || defined(TARGET_NUCLEO_L152RE) || defined(TARGET_NUCLEO_L073RZ)\ + || defined(TARGET_NUCLEO_L053R8) + // added by JH1PJL 2017-9-21 + static void standby_then_reset(uint32_t ms); + +#endif + +private: + static Callback<void()> callback; + static void irq_handler(void); + static float cycles_per_ms; + static bool use_reset; // added by JH1PJL 2017-9-21 +}; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Tue Dec 26 21:30:09 2017 +0000 @@ -0,0 +1,159 @@ +/* This is a project for madelectronengineering.com + This code is used for a hardware monitoring system for the Hybrid Supercapacitor Car Battery to monitor the supercap voltage, the LiFeP04 voltage, +current, and wattage, plus also monitor 3 seperate temperature points. + +Required hardware: + STM32 NUCLEO-L073RZ + TMP36 Temperature Sensors (x3) + INA219 Current & Voltage Breakout (modify by removing 0.1Ohm resistor and replacing with 0.01Ohm Resistor to measure up to 32Amps + ST NUCLEO Protoboard + 30k & 7.5k resistors for voltage divider to measure supercap voltage + 0.1Ohm 25W current limiting power resistor in between supercap array and LiFeP04 battery + Sharp Memory LCD 400x240 + +The INA219 measures the current and amperage on the battery side of the current limiting resistor to keep an eye on the LiFeP04 battery. +voltage divider connects directly to + of supercap array + +Arduino equivelant connections: +A0 - TMP36 #1 +A1 - TMP36 #2 +A2 - TMP36 #3 +A3 - Voltage divider Input +A4&A5 - I2C for INA219 + +Arduino equivelant connections for LCD Screen: +D13 - SClk +D11 - MOSI +D10 - CD +D9 - Enable +D8 - Extcom + +*/ +#include "mbed.h" +#include "SharpLCD.h" +#include "INA219.hpp" +#include "Neu44x36.h" +#include "Neu31x26.h" +#include "WakeUp.h" + +SharpLCD display(PA_7, NC, PA_5, PB_6, PC_7, PA_9); //mosi, miso(not used), sck, cs, enable, extcom +INA219 ina219(PC_1, PC_0, 0x40, 400000, RES_12BITS); +AnalogIn ain1(PA_0); // connect A0 to Vout(Temp36) +AnalogIn ain2(PA_1); // connect A1 to Vout(Temp36) +AnalogIn ain3(PA_4); // connect A2 to Vout(Temp36) +AnalogIn ain4(PB_0); // Connect A3 to Voltage Divider + +int main() +{ + display.enableDisplay(); //enable sharp memory lcd + display.clearImmediate(); //clear the screen buffer and screen + + display.set_font(Neu44x36); + + //only send graphics and text once to the screen during bootup + display.locate(17,0); + display.printf("Hybrid Supercap"); + display.locate(75,35); + display.printf("Car Battery"); + + display.rect(0,73,195,239, Black); + display.rect(205,73,399,239, Black); + display.fillrect(195,73,10,167, Black); + display.line(1,174,194,174, Black); + display.line(1,176,194,176, Black); + display.line(1,107,194,107, Black); + display.line(206,107,399,107, Black); + display.line(1,141,194,141, Black); + display.line(206,141,399,141, Black); + display.line(1,175,194,175, Black); + display.line(206,175,399,175, Black); + display.line(1,209,194,209, Black); + display.line(206,209,399,209, Black); + + display.set_font(Neu31x26); + display.locate(15,76); + display.printf("Supercaps"); + display.locate(240,76); + display.printf("LiFePo4"); + display.locate(30,179); + display.printf("Resistor"); + + display.locate(360,110); + display.printf("V"); + display.locate(360,143); + display.printf("A"); + display.locate(360,177); + display.printf("W"); + display.locate(155,212); + display.printf("'F"); + display.locate(360,212); + display.printf("'F"); + display.locate(155,143); + display.printf("'F"); + display.locate(155,110); + display.printf("V"); + + while(1) { + + float volt; + float current_ma; + float power; + + volt = ina219.read_bus_voltage(); + current_ma = ina219.read_current_mA() / 1000; + power = volt * current_ma; + + float V1 = ain1.read() * 3.3; // connect Vs(Tmp36) to 3.3V + float tempC1 = (V1-0.5) * 100; // calculate temperature C + float tempF1 = (tempC1 * 9 / 5) + 32.0; // calculate temperature F + + float V2 = ain2.read() * 3.3; // connect Vs(Tmp36) to 3.3V + float tempC2 = (V2-0.5) * 100; // calculate temperature C + float tempF2 = (tempC2 * 9 / 5) + 32.0; // calculate temperature F + + float V3 = ain3.read() * 3.3; // connect Vs(Tmp36) to 3.3V + float tempC3 = (V3-0.5) * 100; // calculate temperature C + float tempF3 = (tempC3 * 9 / 5) + 32.0; // calculate temperature F + + float V4 = (ain4.read()) * 3.3; // Voltage divider for supercap voltage + float voltage = (V4 * 5); // Voltage divider is 1 to 5 ratio + + display.set_font(Neu31x26); + display.locate(210,110); + display.printf(" "); + display.locate(210,110); + display.printf("%+05.2f", volt); + display.locate(210,143); + display.printf(" "); + display.locate(210,143); + display.printf("%+04.1f", current_ma); + display.locate(210,177); + display.printf(" "); + display.locate(210,177); + display.printf("%+05.1f", power); + display.locate(20,212); + display.printf(" "); + display.locate(20,212); + display.printf("%+03.0f", tempF1); + display.locate(210,212); + display.printf(" "); + display.locate(210,212); + display.printf("%+03.0f", tempF2); + display.locate(20,143); + display.printf(" "); + display.locate(20,143); + display.printf("%+03.0f", tempF3); + display.locate(20,110); + display.printf(" "); + display.locate(20,110); + display.printf("%+05.2f", voltage); + + display.update(); + +//Set wakeup time for 1 second +// WakeUp::set_ms(500); + + //Enter deepsleep, the program won't go beyond this point until it is woken up +// deepsleep(); +} +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Tue Dec 26 21:30:09 2017 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/e7ca05fa8600 \ No newline at end of file