diff --git a/Core/Inc/eeprom.h b/Core/Inc/eeprom.h index c4daf48..84a007a 100644 --- a/Core/Inc/eeprom.h +++ b/Core/Inc/eeprom.h @@ -11,7 +11,7 @@ #define EEPROM_VAR_ID (0) // 16b: Init ID: 0xCA1C #define EEPROM_VAR_CFG (1) // 16b: CFG(15:8) + INT_CFG(7:0) reg -#define EEPROM_VAR_KBD (2) // 32b: DEB(15:0) + 0x00 + FRQ(7:0) regs +#define EEPROM_VAR_KBD (2) // 32b: DEB(15:0) + 0x00 + FRQ(7:0) regs #define EEPROM_VAR_BCKL (3) // 16b: LCD(15:8) + KBD(7:0) backlight step indice //-------------------------------------------library configuration------------------------------------------- diff --git a/Core/Inc/hal_interface.h b/Core/Inc/hal_interface.h index 54e5d02..3c441b3 100644 --- a/Core/Inc/hal_interface.h +++ b/Core/Inc/hal_interface.h @@ -128,12 +128,29 @@ extern "C" { #define PICO_SDA_GPIO_Port GPIOB +// Structure definition --------------------------------------------------------------- +typedef union { + uint32_t raw; + RTC_TimeTypeDef _s; +} RTC_TimeTypeDef_u; + +typedef union { + uint32_t raw; + RTC_DateTypeDef _s; +} RTC_DateTypeDef_u; + + // Global variables definition -------------------------------------------------------- extern volatile uint32_t systicks_counter; extern volatile uint8_t pmu_irq; +extern volatile uint8_t stop_mode_active; + +extern volatile RTC_TimeTypeDef_u rtc_alarm_time; +extern volatile RTC_DateTypeDef_u rtc_alarm_date; // Global functions definition -------------------------------------------------------- +void SystemClock_Config(void); HAL_StatusTypeDef HAL_Interface_init(void); __STATIC_INLINE uint32_t uptime_ms(void) { return systicks_counter; } diff --git a/Core/Inc/regs.h b/Core/Inc/regs.h index efa6b0f..fb57dc3 100644 --- a/Core/Inc/regs.h +++ b/Core/Inc/regs.h @@ -14,20 +14,18 @@ enum reg_id { REG_ID_BKL = 0x05, // backlight steps (0-9) REG_ID_DEB = 0x06, // debounce cfg REG_ID_FRQ = 0x07, // poll freq cfg - REG_ID_RST = 0x08, // STM32 full reset + REG_ID_PWR_CTRL = 0x08, // Power control (0: idle, 1: pico reset, 2: system reset, 3: reserved, 4: sleep, 5: full-shutdown) REG_ID_FIF = 0x09, // fifo REG_ID_BK2 = 0x0A, // keyboard backlight (0-9) REG_ID_BAT = 0x0B, // battery REG_ID_C64_MTX = 0x0C,// read c64 matrix REG_ID_C64_JS = 0x0D, // joystick io bits - REG_ID_RST_PICO = 0x0E, // Pico reset - REG_ID_SHTDW = 0x0F, // self-shutdown - REG_ID_INT_CFG = 0x10, // IRQ config - REG_ID_RTC_CFG = 0x11, // RTC general config - REG_ID_RTC_DATE = 0x12, // RTC date - REG_ID_RTC_TIME = 0x13, // RTC time - REG_ID_RTC_ALARM_DATE = 0x14, // RTC alarm date - REG_ID_RTC_ALARM_TIME = 0x15, // RTC alarm time + REG_ID_INT_CFG = 0x0E, // IRQ config + REG_ID_RTC_CFG = 0x0F, // RTC general config + REG_ID_RTC_DATE = 0x10, // RTC date + REG_ID_RTC_TIME = 0x11, // RTC time + REG_ID_RTC_ALARM_DATE = 0x12, // RTC alarm date + REG_ID_RTC_ALARM_TIME = 0x13, // RTC alarm time #else REG_ID_CFG = 0x02, //!< config REG_ID_INT_CFG = 0x03, //!< IRQ config @@ -36,6 +34,7 @@ enum reg_id { REG_ID_BK2 = 0x06, //!< keyboard backlight (0-9) REG_ID_DEB = 0x07, //!< debounce cfg (time in ms) REG_ID_FRQ = 0x08, //!< poll freq cfg (time in ms) + REG_ID_PWR_CTRL = 0x09, //!< Power control (0: idle, 1: pico reset, 2: system reset, 3: reserved, 4: sleep, 5: full-shutdown) REG_ID_RTC_CFG = 0x0A, //!< RTC general config REG_ID_RTC_DATE = 0x0B, //!< RTC date @@ -48,10 +47,6 @@ enum reg_id { REG_ID_C64_MTX = 0x12, //!< read c64 matrix REG_ID_C64_JS = 0x13, //!< joystick io bits - REG_ID_RST = 0x20, //!< STM32 full reset - REG_ID_RST_PICO = 0x21, //!< Pico reset - REG_ID_SHTDW = 0x22, //!< self-shutdown - REG_ID_BAT = 0x30, //!< battery #endif REG_ID_LAST @@ -72,6 +67,12 @@ enum reg_id { #define KEY_NUMLOCK (1 << 6) #define KEY_COUNT_MASK 0x1F //0x1F == 31 +#define RTC_CFG_RUN_ALARM (1 << 0) // b0: Set the RTC alarm active. +#define RTC_CFG_REARM (1 << 1) // b1: If set, the RTC alarm will rearm for the next day trigger (if RTC_CFG_DATE_ALARM is set, repeat every day after the target date is reached) +#define RTC_CFG_DATE_ALARM (1 << 2) // b2: If set, check when alarm trig for the precise date, otherwise return to normal behavior (or sleep if wake-up) +#define RTC_CFG_PBTN_ALARM_IGNORE (1 << 3) // b3: If unset, override the power button shutdown behavior to keep the STM32 "alive" for the RTC to operate. +//#define RTC_CFG_SLEEP_MODE (1 << 4) // 0 = normal sleep (pico is shutdown, STM32 enter in low power state ~1.2mA); 1 = deep sleep (pico is shutdown, STM32 enter a stop state ~24uA) + uint8_t reg_get_value(enum reg_id reg); uint8_t* reg_raw_access(void); diff --git a/Core/Inc/rtc.h b/Core/Inc/rtc.h new file mode 100644 index 0000000..91815ce --- /dev/null +++ b/Core/Inc/rtc.h @@ -0,0 +1,15 @@ +#include "stm32f1xx_hal.h" + + +#ifndef RTC_H_ +#define RTC_H_ + +void i2cs_fill_buffer_RTC_date(uint8_t* const buff, const volatile RTC_DateTypeDef* const date_s); +void i2cs_fill_buffer_RTC_time(uint8_t* const buff, const volatile RTC_TimeTypeDef* const time_s); +void i2cs_RTC_date_from_buffer(volatile RTC_DateTypeDef* const date_s, const uint8_t* const buff); +void i2cs_RTC_time_from_buffer(volatile RTC_TimeTypeDef* const time_s, const uint8_t* const buff); + +uint32_t rtc_run_alarm(void); +uint32_t rtc_stop_alarm(void); + +#endif /* RTC_H_ */ diff --git a/Core/Src/hal_interface.c b/Core/Src/hal_interface.c index 2e4d66a..10c9a46 100644 --- a/Core/Src/hal_interface.c +++ b/Core/Src/hal_interface.c @@ -18,6 +18,7 @@ */ #include "hal_interface.h" +#include "stm32f1xx_hal_flash_ex.h" I2C_HandleTypeDef hi2c1; @@ -36,6 +37,9 @@ UART_HandleTypeDef huart1; UART_HandleTypeDef huart3; #endif +volatile RTC_TimeTypeDef_u rtc_alarm_time = {.raw = 0x00000000}; +volatile RTC_DateTypeDef_u rtc_alarm_date = {.raw = 0x00010101}; + static void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim); @@ -125,7 +129,6 @@ static void MX_I2C2_Init(void) { * @retval None */ static void MX_IWDG_Init(void) { -#ifndef DEBUG LL_IWDG_Enable(IWDG); LL_IWDG_EnableWriteAccess(IWDG); LL_IWDG_SetPrescaler(IWDG, LL_IWDG_PRESCALER_32); @@ -133,7 +136,6 @@ static void MX_IWDG_Init(void) { while (LL_IWDG_IsReady(IWDG) != 1) {} LL_IWDG_ReloadCounter(IWDG); -#endif } /** @@ -149,7 +151,7 @@ static void MX_RTC_Init(void) { */ hrtc.Instance = RTC; hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND; - hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM; + hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE; if (HAL_RTC_Init(&hrtc) != HAL_OK) Error_Handler(); @@ -171,6 +173,20 @@ static void MX_RTC_Init(void) { Error_Handler(); } +/** + * @brief RTC fake initialization Function, used when RTC is already alive (after a wake-up reset) + * @param None + * @retval None + */ +static void MX_RTC_Init2(void) { + hrtc.Instance = RTC; + hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND; + hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE; + hrtc.Lock = HAL_UNLOCKED; + HAL_RTC_MspInit(&hrtc); + hrtc.State = HAL_RTC_STATE_READY; +} + /** * @brief TIM1 Initialization Function * @param None @@ -362,7 +378,7 @@ static void MX_GPIO_Init(void) { /**/ GPIO_InitStruct.Pin = SYS_LED_Pin; GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT; - GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM; + GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN; LL_GPIO_Init(SYS_LED_GPIO_Port, &GPIO_InitStruct); @@ -782,7 +798,9 @@ void assert_failed(uint8_t *file, uint32_t line) void flash_one_time(uint32_t ts, uint8_t restore_status) { for (size_t i = 0; i < ts; i++) { - LL_IWDG_ReloadCounter(IWDG); +//#ifndef DEBUG +// LL_IWDG_ReloadCounter(IWDG); +//#endif LL_GPIO_ResetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); HAL_Delay(400); LL_GPIO_SetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); @@ -801,6 +819,7 @@ void flash_one_time(uint32_t ts, uint8_t restore_status) { */ HAL_StatusTypeDef HAL_Interface_init(void) { HAL_StatusTypeDef result = HAL_OK; + FLASH_OBProgramInitTypeDef flash_s; result |= HAL_Init(); if (result != HAL_OK) @@ -808,16 +827,42 @@ HAL_StatusTypeDef HAL_Interface_init(void) { SystemClock_Config(); + // Control wake-up state + //if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST)) + //__HAL_RCC_CLEAR_RESET_FLAGS(); + // Use SRAM backup registers to check if we have soft-reset + HAL_PWR_EnableBkUpAccess(); + MX_GPIO_Init(); MX_I2C1_Init(); MX_I2C2_Init(); - MX_RTC_Init(); + if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0) + MX_RTC_Init2(); + else + MX_RTC_Init(); MX_USART1_UART_Init(); MX_USART3_UART_Init(); - MX_IWDG_Init(); +//#ifndef DEBUG +// MX_IWDG_Init(); +//#endif MX_TIM1_Init(); MX_TIM3_Init(); MX_TIM2_Init(); + // Check options registers values + HAL_FLASHEx_OBGetConfig(&flash_s); + //if ((flash_s.USERConfig & (OB_STOP_NO_RST | OB_STDBY_NO_RST)) != 0) { + if ((flash_s.USERConfig & (OB_STOP_NO_RST | OB_STDBY_NO_RST)) == 0) { + HAL_FLASH_Unlock(); + HAL_FLASH_OB_Unlock(); + //flash_s.USERConfig &= (uint8_t)~(OB_STOP_NO_RST | OB_STDBY_NO_RST); // Enable reset when sleep + flash_s.USERConfig |= (uint8_t)(OB_STOP_NO_RST | OB_STDBY_NO_RST); // Disable reset when sleep + HAL_FLASHEx_OBProgram(&flash_s); + HAL_FLASH_OB_Launch(); // Reset system + + // We should never reach this point + for ( ;; ) {} + } + return result; } diff --git a/Core/Src/main.c b/Core/Src/main.c index e0f36a7..6bec462 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -11,9 +11,16 @@ * * SYS_LED and COL_x are open-drain, output logic is inverted. * + * Unless requested by the user through REG_ID_PWR_CTRL register or by removing + * the batteries, pressing the power button when running now put the STM32 in + * low-power stop mode and shutdown the PICO board. + * It's the only mean to keep the RTC functional. + * A full shutdown using AXP2101 power-off or by removing the batteries will + * reset the RTC and disable auto wake-up feature. + * */ -#include "hal_interface.h" +#include "hal_interface.h" #include "axp2101.h" #include "backlight.h" #include "batt.h" @@ -21,6 +28,7 @@ #include "fifo.h" #include "keyboard.h" #include "regs.h" +#include "rtc.h" // Private define ------------------------------------------------------------ @@ -30,6 +38,7 @@ #define DEFAULT_KBD_BL (0) //step-1 (0%) #define DEFAULT_KBD_FREQ (KEY_POLL_TIME) #define DEFAULT_KBD_DEB (KEY_HOLD_TIME) +#define DEFAULT_RCT_CFG (0x00) #define I2CS_REARM_TIMEOUT 500 #define I2CS_W_BUFF_LEN 31+1 // The last one must be only a 0 value, TODO: another cleaner way? @@ -82,8 +91,9 @@ static enum i2cs_state i2cs_state = I2CS_STATE_IDLE; static uint8_t keycb_start = 0; static uint32_t head_phone_status = 0; // TODO: Combine status registers -static volatile RTC_TimeTypeDef rtc_alarm_time = {0}; -static volatile RTC_DateTypeDef rtc_alarm_date = {0}; +static volatile uint8_t rtc_reg_xor_events = 0; + +volatile uint8_t stop_mode_active = 0; volatile uint8_t pmu_irq = 0; static uint32_t pmu_online = 0; @@ -98,10 +108,8 @@ static void sync_bat(void); static void printPMU(void); #endif static void check_pmu_int(void); -static void i2cs_fill_buffer_RTC_date(uint8_t* const buff, const volatile RTC_DateTypeDef* const date_s); -static void i2cs_fill_buffer_RTC_time(uint8_t* const buff, const volatile RTC_TimeTypeDef* const time_s); -static void i2cs_RTC_date_from_buffer(volatile RTC_DateTypeDef* const date_s, const uint8_t* const buff); -static void i2cs_RTC_time_from_buffer(volatile RTC_TimeTypeDef* const time_s, const uint8_t* const buff); +static void sys_prepare_sleep(void); +static void sys_wake_sleep(void); extern void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim == &htim2) { @@ -168,6 +176,8 @@ extern void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirect i2cs_w_buff[0] = item.state; i2cs_w_buff[1] = item.key; } else if (reg == REG_ID_INT) { + if (is_write) + reg_set_value(REG_ID_INT, 0); i2cs_w_buff[1] = reg_get_value(REG_ID_INT); LL_GPIO_SetOutputPin(PICO_IRQ_GPIO_Port, PICO_IRQ_Pin); // De-assert the IRQ signal } else if (reg == REG_ID_VER) { @@ -177,8 +187,10 @@ extern void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirect } else if (reg == REG_ID_BAT) { i2cs_w_buff[1] = reg_get_value(REG_ID_BAT); } else if (reg == REG_ID_RTC_CFG) { - if (is_write) + if (is_write) { + rtc_reg_xor_events |= reg_get_value(REG_ID_RTC_CFG) ^ i2cs_r_buff[1]; // Using a "OR" set style to avoid loosing change before processing it reg_set_value(REG_ID_RTC_CFG, i2cs_r_buff[1]); + } i2cs_w_buff[1] = reg_get_value(REG_ID_RTC_CFG); } else if (reg == REG_ID_RTC_DATE) { RTC_DateTypeDef date_s = {0}; @@ -206,16 +218,16 @@ extern void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirect i2cs_w_len = 4; } else if (reg == REG_ID_RTC_ALARM_DATE) { if (is_write) - i2cs_RTC_date_from_buffer(&rtc_alarm_date, &i2cs_r_buff[1]); + i2cs_RTC_date_from_buffer(&rtc_alarm_date._s, &i2cs_r_buff[1]); - i2cs_fill_buffer_RTC_date(&i2cs_w_buff[1], &rtc_alarm_date); + i2cs_fill_buffer_RTC_date(&i2cs_w_buff[1], &rtc_alarm_date._s); i2cs_w_len = 5; } else if (reg == REG_ID_RTC_ALARM_TIME) { if (is_write) - i2cs_RTC_time_from_buffer(&rtc_alarm_time, &i2cs_r_buff[1]); + i2cs_RTC_time_from_buffer(&rtc_alarm_time._s, &i2cs_r_buff[1]); - i2cs_fill_buffer_RTC_time(&i2cs_w_buff[1], &rtc_alarm_time); + i2cs_fill_buffer_RTC_time(&i2cs_w_buff[1], &rtc_alarm_time._s); i2cs_w_len = 4; } else if (reg == REG_ID_KEY) { @@ -231,20 +243,10 @@ extern void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirect i2cs_w_len = 10; } else if (reg == REG_ID_C64_JS) { i2cs_w_buff[1] = js_bits; - } else if (reg == REG_ID_RST) { + } else if (reg == REG_ID_PWR_CTRL) { if (is_write) - reg_set_value(REG_ID_RST, 1); - i2cs_w_buff[1] = reg_get_value(REG_ID_RST); - } else if (reg == REG_ID_RST_PICO) { - if (is_write) - reg_set_value(REG_ID_RST_PICO, 1); - i2cs_w_buff[1] = reg_get_value(REG_ID_RST_PICO); - } else if (reg == REG_ID_SHTDW) { - if (is_write) { - reg_set_value(REG_ID_SHTDW, 1); - return; // Ignore answer, everything will be shutdown - } - i2cs_w_buff[1] = 0; + reg_set_value(REG_ID_PWR_CTRL, i2cs_r_buff[1]); + i2cs_w_buff[1] = reg_get_value(REG_ID_PWR_CTRL); } else { i2cs_w_buff[0] = 0; i2cs_w_buff[1] = 0; @@ -338,12 +340,12 @@ int main(void) { if (result != HAL_OK) Error_Handler(); - LL_GPIO_ResetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); // I'm alive! - // Start the systick timer if (HAL_TIM_Base_Start_IT(&htim2) != HAL_OK) Error_Handler(); + LL_GPIO_ResetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); // I'm alive! + // EEPROM emulation init if (EEPROM_Init() != EEPROM_SUCCESS) Error_Handler(); @@ -363,6 +365,15 @@ int main(void) { #endif } + // Check RTC SRAM first run + if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) == 0) { + HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0xCA1C); + HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, ((rtc_alarm_time.raw & 0xFF) << 8) | DEFAULT_RCT_CFG); + HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, rtc_alarm_time.raw >> 16); + HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR4, rtc_alarm_date.raw & 0xFFFF); + HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, rtc_alarm_date.raw >> 16); + } + // I2C-Pico interface registers reg_init(); HAL_Delay(10); @@ -431,6 +442,7 @@ int main(void) { AXP2101_clearIrqStatus(); AXP2101_enableIRQ(XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_BAT_REMOVE_IRQ | // BATTERY + XPOWERS_AXP2101_WARNING_LEVEL1_IRQ | XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_VBUS_REMOVE_IRQ | // VBUS XPOWERS_AXP2101_PKEY_SHORT_IRQ | @@ -460,7 +472,9 @@ int main(void) { low_bat(); while (1) { - LL_IWDG_ReloadCounter(IWDG); +//#ifndef DEBUG +// LL_IWDG_ReloadCounter(IWDG); +//#endif // Re-arm I2CS in case of lost master signal if (i2cs_state != I2CS_STATE_IDLE && ((uptime_ms() - i2cs_rearm_counter) > I2CS_REARM_TIMEOUT)) @@ -471,36 +485,89 @@ int main(void) { keyboard_process(); hw_check_HP_presence(); + // Check RTC new events to process + if (rtc_reg_xor_events != 0) { + if ((rtc_reg_xor_events & RTC_CFG_RUN_ALARM) == RTC_CFG_RUN_ALARM) { + if (reg_get_value(REG_ID_RTC_CFG) & RTC_CFG_RUN_ALARM) { + if (rtc_run_alarm() != HAL_OK) + reg_set_value(REG_ID_RTC_CFG, reg_get_value(REG_ID_RTC_CFG) & (uint8_t)~RTC_CFG_RUN_ALARM); + } else { + if (rtc_stop_alarm() != HAL_OK) + reg_set_value(REG_ID_RTC_CFG, reg_get_value(REG_ID_RTC_CFG) | RTC_CFG_RUN_ALARM); + } + + rtc_reg_xor_events &= (uint8_t)~RTC_CFG_RUN_ALARM; + } + } + // Check internal status - if (reg_get_value(REG_ID_SHTDW) == 1) { // Nominal full system shutdown as requested from I2C bus - reg_set_value(REG_ID_SHTDW, 0); + switch (reg_get_value(REG_ID_PWR_CTRL)) { + case 1: + reg_set_value(REG_ID_PWR_CTRL, 0); + HAL_Delay(200); // Wait for final I2C answer + if (HAL_I2C_DisableListen_IT(&hi2c1) != HAL_OK) + Error_Handler(); + LL_GPIO_ResetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); + LL_GPIO_ResetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); + + HAL_Delay(200); // No need to use keyboard, so a simple delay should suffice + LL_GPIO_SetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); + LL_GPIO_SetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); + if (HAL_I2C_EnableListen_IT(&hi2c1) != HAL_OK) + Error_Handler(); + break; + + case 2: + reg_set_value(REG_ID_PWR_CTRL, 0); + HAL_Delay(200); // Wait for final I2C answer + if (HAL_I2C_DisableListen_IT(&hi2c1) != HAL_OK) + Error_Handler(); + LL_GPIO_ResetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); + LL_GPIO_ResetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); + + NVIC_SystemReset(); + break; + + //case 3: + + case 4: + reg_set_value(REG_ID_PWR_CTRL, 0); + LL_GPIO_ResetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); + LL_GPIO_ResetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); + AXP2101_setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG); + + stop_mode_active = 1; + break; + + case 5: + reg_set_value(REG_ID_PWR_CTRL, 0); + LL_GPIO_ResetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); + LL_GPIO_ResetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); + AXP2101_setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG); + + AXP2101_shutdown(); // Full shudown will rip the RTC configuration! Need to be reset at next reboot. + break; + + default: + break; + } + + if (stop_mode_active == 1) { + /* Prepare peripherals to the low-power mode */ + sys_prepare_sleep(); + + /* Low-power mode entry */ + //HAL_WWDG_Disable(); + HAL_SuspendTick(); + HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); + SystemClock_Config(); + HAL_ResumeTick(); LL_GPIO_ResetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); LL_GPIO_ResetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); - AXP2101_setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG); + HAL_Delay(500); - AXP2101_shutdown(); - } else if (reg_get_value(REG_ID_RST) == 1) { // Try to reset only the STM32 - reg_set_value(REG_ID_RST, 0); - HAL_Delay(200); // Wait for final I2C answer - if (HAL_I2C_DisableListen_IT(&hi2c1) != HAL_OK) - Error_Handler(); - LL_GPIO_ResetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); - LL_GPIO_ResetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); - - NVIC_SystemReset(); - } else if (reg_get_value(REG_ID_RST_PICO) == 1) { // Reset only the Pico - reg_set_value(REG_ID_RST_PICO, 0); - HAL_Delay(200); // Wait for final I2C answer - if (HAL_I2C_DisableListen_IT(&hi2c1) != HAL_OK) - Error_Handler(); - LL_GPIO_ResetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); - LL_GPIO_ResetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); - - HAL_Delay(200); // No need to use keyboard, so a simple delay should suffice - LL_GPIO_SetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); - LL_GPIO_SetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); - if (HAL_I2C_EnableListen_IT(&hi2c1) != HAL_OK) - Error_Handler(); + /* Wake-up peripherals from low-power mode */ + sys_wake_sleep(); } } } @@ -795,7 +862,13 @@ __STATIC_INLINE void check_pmu_int(void) { printPMU(); #endif - // enterPmuSleep(); //TODO: implement sleep mode, RTC, etc.? + if (stop_mode_active == 1) { + stop_mode_active = 0; + LL_GPIO_SetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); + LL_GPIO_SetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); + } else { + // enterPmuSleep(); //TODO: replace by pico reset if Shift key is pressed + } } if (AXP2101_isPkeyLongPressIrq()) { #ifdef DEBUG @@ -805,10 +878,16 @@ __STATIC_INLINE void check_pmu_int(void) { //uint8_t data[4] = {1, 2, 3, 4}; //PMU.writeDataBuffer(data, XPOWERS_AXP2101_DATA_BUFFER_SIZE); - LL_GPIO_ResetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); - LL_GPIO_ResetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); - AXP2101_setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG); - AXP2101_shutdown(); + if (stop_mode_active == 1) { + stop_mode_active = 0; + LL_GPIO_SetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); + LL_GPIO_SetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); + } else { + LL_GPIO_ResetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); + LL_GPIO_ResetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); + AXP2101_setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG); + stop_mode_active = 1; + } } /*if (PMU.isPekeyNegativeIrq()) { @@ -866,40 +945,48 @@ __STATIC_INLINE void check_pmu_int(void) { } } -static void i2cs_fill_buffer_RTC_date(uint8_t* const date_buff, const volatile RTC_DateTypeDef* const date_s) { - if (date_s == NULL || date_buff == NULL) - return; +__STATIC_INLINE void sys_prepare_sleep(void) { + LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; - date_buff[0] = date_s->Year; - date_buff[1] = date_s->Month; - date_buff[2] = date_s->Date; - date_buff[3] = date_s->WeekDay; + AXP2101_disableIRQ(XPOWERS_AXP2101_ALL_IRQ); + AXP2101_clearIrqStatus(); + AXP2101_enableIRQ(XPOWERS_AXP2101_PKEY_SHORT_IRQ | + XPOWERS_AXP2101_PKEY_LONG_IRQ | + XPOWERS_AXP2101_WARNING_LEVEL1_IRQ + ); + lcd_backlight_off(); + kbd_backlight_off(); + HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1); + HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_3); + GPIO_InitStruct.Pin = SYS_LED_Pin; + GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT; + GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + LL_GPIO_Init(SYS_LED_GPIO_Port, &GPIO_InitStruct); + LL_GPIO_SetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); } -static void i2cs_fill_buffer_RTC_time(uint8_t* const time_buff, const volatile RTC_TimeTypeDef* const time_s) { - if (time_s == NULL || time_buff == NULL) - return; +__STATIC_INLINE void sys_wake_sleep(void) { + GPIO_InitTypeDef GPIO_InitStruct = {0}; - time_buff[0] = time_s->Hours; - time_buff[1] = time_s->Minutes; - time_buff[2] = time_s->Seconds; -} - -static void i2cs_RTC_date_from_buffer(volatile RTC_DateTypeDef* const date_s, const uint8_t* const date_buff) { - if (date_s == NULL || date_buff == NULL) - return; - - date_s->Year = date_buff[0] <= 99? date_buff[0] : 99; - date_s->Month = (date_buff[1] > 0 && date_buff[1] <= 12)? date_buff[1] : 12; - date_s->Date = (date_buff[2] > 0 && date_buff[2] <= 99)? date_buff[2] : 99; - //data_s.WeekDay - this element is automatically recomputed -} - -static void i2cs_RTC_time_from_buffer(volatile RTC_TimeTypeDef* const time_s, const uint8_t* const time_buff) { - if (time_s == NULL || time_buff == NULL) - return; - - time_s->Hours = time_buff[0] <= 23 ? time_buff[0] : 23; - time_s->Minutes = time_buff[1] <= 59 ? time_buff[1] : 59; - time_s->Seconds = time_buff[2] <= 59 ? time_buff[2] : 59; + GPIO_InitStruct.Pin = SYS_LED_Pin; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; + GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM; + HAL_GPIO_Init(SYS_LED_GPIO_Port, &GPIO_InitStruct); + LL_GPIO_ResetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); + LL_GPIO_ResetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); + HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); + HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3); + HAL_Delay(300); + kbd_backlight_on(); + lcd_backlight_on(); + AXP2101_enableIRQ(XPOWERS_AXP2101_BAT_INSERT_IRQ | + XPOWERS_AXP2101_BAT_REMOVE_IRQ | // BATTERY + XPOWERS_AXP2101_VBUS_INSERT_IRQ | + XPOWERS_AXP2101_VBUS_REMOVE_IRQ | // VBUS + XPOWERS_AXP2101_PKEY_SHORT_IRQ | + XPOWERS_AXP2101_PKEY_LONG_IRQ | // POWER KEY + XPOWERS_AXP2101_BAT_CHG_DONE_IRQ | + XPOWERS_AXP2101_BAT_CHG_START_IRQ // CHARGE + ); } diff --git a/Core/Src/regs.c b/Core/Src/regs.c index a14d383..0828b04 100644 --- a/Core/Src/regs.c +++ b/Core/Src/regs.c @@ -1,10 +1,13 @@ #include "regs.h" #include "hal_interface.h" +#include "stm32f1xx_hal_rtc_ex.h" #include "eeprom.h" #include "version.h" +extern RTC_HandleTypeDef hrtc; + static uint8_t regs[REG_ID_LAST] = {0}; static uint8_t regs_unsync[REG_ID_LAST] = {0}; static uint32_t eeprom_refresh_counter; @@ -58,7 +61,7 @@ inline void reg_set_bit(enum reg_id reg, uint8_t bit) { | 0 | CFG_OVERFLOW_ON | When a FIFO overflow happens, should the new entry still be pushed, overwriting the oldest one. If 0 then new entry is lost. | */ void reg_init(void) { - uint16_t buff; + uint32_t buff; regs[REG_ID_VER] = (uint8_t)((VERSION_MAJOR << 4) | VERSION_MINOR); // 1.2 => (0x1 << 4) | 0x2 @@ -74,6 +77,17 @@ void reg_init(void) { regs[REG_ID_BKL] = (uint8_t)((buff >> 8) & 0xFF); regs[REG_ID_BK2] = (uint8_t)(buff & 0xFF); + buff = 0; + buff |= HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2); + buff |= HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3) << 16; + regs[REG_ID_RTC_CFG] = (uint8_t)(buff & 0xFF); + rtc_alarm_time.raw = (uint8_t)((buff >> 8) & 0xFFFFFF); + + buff = 0; + buff |= HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4); + buff |= HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR5) << 16; + rtc_alarm_date.raw = buff; + regs[REG_ID_BAT] = 0; //default .no battery ,no charging regs[REG_ID_TYP] = 0xCA; // That's me :3 @@ -101,6 +115,16 @@ uint32_t reg_check_and_save_eeprom(void) { if (regs_unsync[REG_ID_BKL] == 1 || regs_unsync[REG_ID_BK2] == 1) result |= EEPROM_WriteVariable(EEPROM_VAR_BCKL, (EEPROM_Value)(uint16_t)((regs[REG_ID_BKL] << 8) | regs[REG_ID_BK2]), EEPROM_SIZE16); + if (regs_unsync[REG_ID_RTC_ALARM_TIME] == 1 || regs_unsync[REG_ID_RTC_CFG] == 1) { + HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, ((rtc_alarm_time.raw & 0xFF) << 8) | regs[REG_ID_RTC_CFG]); + HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, rtc_alarm_time.raw >> 16); + } + + if (regs_unsync[REG_ID_RTC_ALARM_DATE] == 1) { + HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR4, rtc_alarm_date.raw & 0xFFFF); + HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, rtc_alarm_date.raw >> 16); + } + for (size_t i = 0; i < REG_ID_LAST; i++) regs_unsync[i] = 0; } diff --git a/Core/Src/rtc.c b/Core/Src/rtc.c new file mode 100644 index 0000000..18e6712 --- /dev/null +++ b/Core/Src/rtc.c @@ -0,0 +1,81 @@ +#include "rtc.h" + +#include "hal_interface.h" +#include "regs.h" + + +extern RTC_HandleTypeDef hrtc; + +extern void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc) { + uint8_t rtc_conf = reg_get_value(REG_ID_RTC_CFG); + RTC_DateTypeDef date_s = {0}; + uint8_t date_valid = 1; + + if ((rtc_conf & RTC_CFG_DATE_ALARM) == RTC_CFG_DATE_ALARM) { + HAL_RTC_GetDate(hrtc, &date_s, RTC_FORMAT_BCD); + if (date_s.Year != rtc_alarm_date._s.Year || + date_s.Month != rtc_alarm_date._s.Month || + date_s.Date != rtc_alarm_date._s.Date) + date_valid = 0; + } + + rtc_stop_alarm(); + if (date_valid == 1) { + if ((rtc_conf & RTC_CFG_REARM) == RTC_CFG_REARM) + rtc_run_alarm(); + } else { + rtc_run_alarm(); + } +} + +void i2cs_fill_buffer_RTC_date(uint8_t* const date_buff, const volatile RTC_DateTypeDef* const date_s) { + if (date_s == NULL || date_buff == NULL) + return; + + date_buff[0] = date_s->Year; + date_buff[1] = date_s->Month; + date_buff[2] = date_s->Date; + date_buff[3] = date_s->WeekDay; +} + +void i2cs_fill_buffer_RTC_time(uint8_t* const time_buff, const volatile RTC_TimeTypeDef* const time_s) { + if (time_s == NULL || time_buff == NULL) + return; + + time_buff[0] = time_s->Hours; + time_buff[1] = time_s->Minutes; + time_buff[2] = time_s->Seconds; +} + +void i2cs_RTC_date_from_buffer(volatile RTC_DateTypeDef* const date_s, const uint8_t* const date_buff) { + if (date_s == NULL || date_buff == NULL) + return; + + date_s->Year = date_buff[0] <= 99? date_buff[0] : 99; + date_s->Month = (date_buff[1] > 0 && date_buff[1] <= 12)? date_buff[1] : 12; + date_s->Date = (date_buff[2] > 0 && date_buff[2] <= 99)? date_buff[2] : 99; + //data_s.WeekDay - this element is automatically recomputed +} + +void i2cs_RTC_time_from_buffer(volatile RTC_TimeTypeDef* const time_s, const uint8_t* const time_buff) { + if (time_s == NULL || time_buff == NULL) + return; + + time_s->Hours = time_buff[0] <= 23 ? time_buff[0] : 23; + time_s->Minutes = time_buff[1] <= 59 ? time_buff[1] : 59; + time_s->Seconds = time_buff[2] <= 59 ? time_buff[2] : 59; +} + +inline uint32_t rtc_run_alarm(void) { + RTC_AlarmTypeDef alarm_s; + + alarm_s.Alarm = RTC_ALARM_A; + alarm_s.AlarmTime.Hours = rtc_alarm_time._s.Hours; + alarm_s.AlarmTime.Minutes = rtc_alarm_time._s.Minutes; + alarm_s.AlarmTime.Seconds = rtc_alarm_time._s.Seconds; + return (uint32_t)HAL_RTC_SetAlarm_IT(&hrtc, &alarm_s, RTC_FORMAT_BCD); +} + +inline uint32_t rtc_stop_alarm(void) { + return (uint32_t)HAL_RTC_DeactivateAlarm(&hrtc, RTC_ALARM_A); +} diff --git a/CubeMX/picocalc_power_sequence.pcs b/CubeMX/picocalc_power_sequence.pcs new file mode 100644 index 0000000..9f715ac --- /dev/null +++ b/CubeMX/picocalc_power_sequence.pcs @@ -0,0 +1,76 @@ +#PCC Sequence - do not modify +#Wed May 21 14:12:50 CEST 2025 +PCC.Battery=16850 +PCC.Battery.Capacity=3000.0 +PCC.Battery.InParallel=2 +PCC.Battery.InSeries=1 +PCC.Battery.SelfDischarge=0.08 +PCC.Checker=false +PCC.Datasheet=DS5319_Rev17 +PCC.Line=STM32F103 +PCC.MCU=STM32F103R(8-B)Tx +PCC.PartNumber=STM32F103R8Tx +PCC.Seq0=4 +PCC.Seq0.Step0.Average_Current=3.41 mA +PCC.Seq0.Step0.CPU_Frequency=4 MHz +PCC.Seq0.Step0.Category=In DS Table +PCC.Seq0.Step0.DMIPS=5.0 +PCC.Seq0.Step0.Duration=0.1 ms +PCC.Seq0.Step0.Frequency=8 MHz +PCC.Seq0.Step0.Memory=FLASH +PCC.Seq0.Step0.Mode=RUN +PCC.Seq0.Step0.Oscillator=HSE +PCC.Seq0.Step0.Peripherals=APB1-Bridge APB2-Bridge GPIOA GPIOB GPIOC GPIOD I2C1 I2C2 PVD/BOR PWR RTC TIM1 TIM2 TIM3 WWDG +PCC.Seq0.Step0.TaMax=104.49 +PCC.Seq0.Step0.User's_Consumption=0 mA +PCC.Seq0.Step0.Vcore=No Scale +PCC.Seq0.Step0.Vdd=3.3 +PCC.Seq0.Step0.Voltage_Source=Battery +PCC.Seq0.Step1.Average_Current=1.7 mA +PCC.Seq0.Step1.CPU_Frequency=4 MHz +PCC.Seq0.Step1.Category=In DS Table +PCC.Seq0.Step1.DMIPS=5.0 +PCC.Seq0.Step1.Duration=1 ms +PCC.Seq0.Step1.Frequency=8 MHz +PCC.Seq0.Step1.Memory=RAM/FLASH +PCC.Seq0.Step1.Mode=SLEEP +PCC.Seq0.Step1.Oscillator=HSE +PCC.Seq0.Step1.Peripherals=APB1-Bridge APB2-Bridge GPIOA GPIOB GPIOC GPIOD I2C1 I2C2 RTC TIM1 TIM2 TIM3 WWDG +PCC.Seq0.Step1.TaMax=104.75 +PCC.Seq0.Step1.User's_Consumption=0 mA +PCC.Seq0.Step1.Vcore=No Scale +PCC.Seq0.Step1.Vdd=3.3 +PCC.Seq0.Step1.Voltage_Source=Battery +PCC.Seq0.Step2.Average_Current=24 \u00B5A +PCC.Seq0.Step2.CPU_Frequency=0 Hz +PCC.Seq0.Step2.Category=In DS Table +PCC.Seq0.Step2.DMIPS=0.0 +PCC.Seq0.Step2.Duration=1 ms +PCC.Seq0.Step2.Frequency=0 Hz +PCC.Seq0.Step2.Memory=n/a +PCC.Seq0.Step2.Mode=STOP +PCC.Seq0.Step2.Oscillator=Regulator_ON +PCC.Seq0.Step2.Peripherals=RTC* +PCC.Seq0.Step2.TaMax=105 +PCC.Seq0.Step2.User's_Consumption=0 mA +PCC.Seq0.Step2.Vcore=No Scale +PCC.Seq0.Step2.Vdd=3.3 +PCC.Seq0.Step2.Voltage_Source=Battery +PCC.Seq0.Step3.Average_Current=2 \u00B5A +PCC.Seq0.Step3.CPU_Frequency=0 Hz +PCC.Seq0.Step3.Category=In DS Table +PCC.Seq0.Step3.DMIPS=0.0 +PCC.Seq0.Step3.Duration=1 ms +PCC.Seq0.Step3.Frequency=0 Hz +PCC.Seq0.Step3.Memory=n/a +PCC.Seq0.Step3.Mode=STANDBY +PCC.Seq0.Step3.Oscillator=ALL CLOCKS OFF +PCC.Seq0.Step3.Peripherals=RTC* +PCC.Seq0.Step3.TaMax=105 +PCC.Seq0.Step3.User's_Consumption=0 mA +PCC.Seq0.Step3.Vcore=No Scale +PCC.Seq0.Step3.Vdd=3.3 +PCC.Seq0.Step3.Voltage_Source=Battery +PCC.Series=STM32F1 +PCC.Temperature=25 +PCC.Vdd=3.3 diff --git a/Makefile b/Makefile index e2aedcb..b8695aa 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,7 @@ Core/Src/eeprom.c \ Core/Src/fifo.c \ Core/Src/keyboard.c \ Core/Src/regs.c \ +Core/Src/rtc.c \ Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio_ex.c \ Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_gpio.c \ Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_i2c.c \ @@ -63,6 +64,7 @@ Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash.c \ Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash_ex.c \ Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_exti.c \ Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rtc.c \ +Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rtc_ex.c \ Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim.c \ Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim_ex.c \ Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_uart.c \