STM32F407ZET6_MRB/Core/Src/user_power_mgmt.c

137 lines
3.4 KiB
C

#include "stm32f4xx_hal.h"
#include "user_power_mgmt.h"
extern TIM_HandleTypeDef htim6;
void user_pm_frequency_update_callback(void);
typedef struct {
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
uint32_t Flash_Latency;
uint32_t Voltage_Scale;
} user_pm_vfs_t;
#include "user_power_mgmt_profiles.h"
static user_pm_vfs_preset_t s_current_preset;
static void user_pm_error_handler(void) {
while(1) {
//
}
}
static void user_pm_tim_callback(TIM_HandleTypeDef *htim) {
// No idea for now, just keep it.
}
/**
* @brief Set VFS mode, bad things happens if wrong sequence applied.
*
* @param vfs
* @return HAL_StatusTypeDef
*/
static HAL_StatusTypeDef user_pm_set_vfs_mode(user_pm_vfs_t *vfs) {
//Temporary raise voltage to SCALE 1
if(HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK) {
return HAL_ERROR;
}
// Set oscillators and PLLs.
if(HAL_RCC_OscConfig(&vfs->RCC_OscInitStruct) != HAL_OK) {
return HAL_ERROR;
}
// Apply system clock settings and bus clock settings.
if(HAL_RCC_ClockConfig(&vfs->RCC_ClkInitStruct, vfs->Flash_Latency) != HAL_OK) {
return HAL_ERROR;
}
// Set correct voltage scale.
if(HAL_PWREx_ControlVoltageScaling(vfs->Voltage_Scale) != HAL_OK) {
return HAL_ERROR;
}
return HAL_OK;
}
/**
* @brief Scale system frequency to specified preset
*
* @param preset: Target VFS preset
* @return Status
*/
HAL_StatusTypeDef user_pm_scale_vfs(user_pm_vfs_preset_t preset) {
if(preset > USER_PM_VFS_END) return HAL_ERROR;
// Transition system to a stable state.
if(user_pm_set_vfs_mode(&s_user_vfs_table[USER_PM_VFS_SAFE]) != HAL_OK) {
// Something bad happened.
user_pm_error_handler();
}
// Apply new frequency settings.
if(user_pm_set_vfs_mode(&s_user_vfs_table[preset]) != HAL_OK) {
// Another bad thing happened.
user_pm_error_handler();
}
// Update global variables
SystemCoreClockUpdate();
// This will happen in a critical section, no context switch will occur.
s_current_preset = preset;
// Call user defined callbacks.
user_pm_frequency_update_callback();
return HAL_OK;
}
/**
* @brief Sleep for specific timeout
*
* @param sleep_msec: Time to sleep
* @return Time actually in sleep mode
*/
uint32_t user_pm_idle_timeout(uint32_t sleep_msec) {
user_pm_vfs_preset_t prev_preset = s_current_preset;
if(user_pm_scale_vfs(USER_PM_VFS_XLOW_2) != HAL_OK) {
user_pm_error_handler();
}
if(sleep_msec > 65535) sleep_msec = 65535; // Cap. Maximum to 65535ms.
if(sleep_msec == 0) sleep_msec = 1;
// Configure TIM6 period.
htim6.Init.Period = sleep_msec;
HAL_TIM_Base_Init(&htim6);
HAL_TIM_RegisterCallback(&htim6, HAL_TIM_PERIOD_ELAPSED_CB_ID, user_pm_tim_callback);
// SysTick uses TIM7, disable TIM7 interrupt.
HAL_SuspendTick();
HAL_TIM_Base_Start_IT(&htim6);
__WFI();
HAL_TIM_Base_Stop_IT(&htim6);
HAL_ResumeTick();
// Restore previous VFS profile
if(user_pm_scale_vfs(prev_preset) != HAL_OK) {
user_pm_error_handler();
}
// In case we woken up by another interrupt, return actual sleep'd time.
return __HAL_TIM_GET_COUNTER(&htim6);
}
__weak void user_pm_frequency_update_callback(void) {
// User application will implement this.
}