RTC working prototype

- Removed IWDG (need to migrate to WWDG for RTC working)
- Power registers to ask for next power state
- Stop mode instead of complete power shutdown
This commit is contained in:
JackCarterSmith 2025-05-24 23:36:16 +02:00
parent 4d93c9d2e2
commit efab6c2ff3
Signed by: JackCarterSmith
GPG Key ID: 832E52F4E23F8F24
10 changed files with 461 additions and 113 deletions

View File

@ -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-------------------------------------------

View File

@ -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; }

View File

@ -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);

15
Core/Inc/rtc.h Normal file
View File

@ -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_ */

View File

@ -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;
}

View File

@ -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
);
}

View File

@ -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;
}

81
Core/Src/rtc.c Normal file
View File

@ -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);
}

View File

@ -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

View File

@ -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 \