275 lines
6.7 KiB
C
275 lines
6.7 KiB
C
/*
|
||
* @Author:
|
||
* @Date: 2023-04-11 18:46:58
|
||
* @LastEditors: xxx
|
||
* @LastEditTime: 2023-08-25 11:31:06
|
||
* @Description:
|
||
* email:
|
||
* Copyright (c) 2023 by xxx, All Rights Reserved.
|
||
*/
|
||
|
||
#include "sys.h"
|
||
#include "delay.h"
|
||
clock_config_t original_clock_config; // 原始时钟配置
|
||
__IO uint32_t uw_tick;
|
||
__IO uint32_t scheduler_start_time; // 调度器开始时间
|
||
__IO uint32_t scheduler_end_time; // 调度器结束时间
|
||
__IO uint32_t scheduler_occupancy_time = 0; // 调度器占用时间
|
||
/**
|
||
* @brief 设置中断向量表偏移地址
|
||
* @param baseaddr : 基址
|
||
* @param offset : 偏移量
|
||
* @retval 无
|
||
*/
|
||
void sys_nvic_set_vector_table(uint32_t baseaddr, uint32_t offset)
|
||
{
|
||
/* 设置NVIC的向量表偏移寄存器,VTOR低9位保留,即[8:0]保留 */
|
||
SCB->VTOR = baseaddr | (offset & (uint32_t)0xFFFFFE00);
|
||
}
|
||
|
||
/**
|
||
* @brief 执行: WFI指令(执行完该指令进入低功耗状态, 等待中断唤醒)
|
||
* @param 无
|
||
* @retval 无
|
||
*/
|
||
void sys_wfi_set(void)
|
||
{
|
||
__ASM __IO("wfi");
|
||
}
|
||
|
||
/**
|
||
* @brief 关闭所有中断(但是不包括fault和NMI中断)
|
||
* @param 无
|
||
* @retval 无
|
||
*/
|
||
void sys_intx_disable(void)
|
||
{
|
||
__ASM __IO("cpsid i");
|
||
}
|
||
|
||
/**
|
||
* @brief 开启所有中断
|
||
* @param 无
|
||
* @retval 无
|
||
*/
|
||
void sys_intx_enable(void)
|
||
{
|
||
__ASM __IO("cpsie i");
|
||
}
|
||
|
||
/**
|
||
* @brief 设置栈顶地址
|
||
* @note 左侧若有红X, 属于MDK误报, 实际是没问题的
|
||
* @param addr: 栈顶地址
|
||
* @retval 无
|
||
*/
|
||
void sys_msr_msp(uint32_t addr)
|
||
{
|
||
__set_MSP(addr); /* 设置栈顶地址 */
|
||
}
|
||
|
||
/**
|
||
* @brief 进入待机模式
|
||
* @param 无
|
||
* @retval 无
|
||
*/
|
||
void sys_standby(void)
|
||
{
|
||
// LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR); /* 使能电源时钟 */
|
||
// SET_BIT(PWR->CR, PWR_CR_PDDS); /* 进入待机模式 */
|
||
}
|
||
|
||
/**
|
||
* @brief 系统软复位
|
||
* @param 无
|
||
* @retval 无
|
||
*/
|
||
void sys_soft_reset(void)
|
||
{
|
||
NVIC_SystemReset();
|
||
}
|
||
|
||
/**
|
||
* @brief 10ms滴答定时器,需要单独放到定时器中断中执行
|
||
* @param 无
|
||
* @retval 提供给sys_millis()函数使用,之前放在SysTick_Handler中执行影响精度
|
||
*/
|
||
__weak void LL_IncTick(void)
|
||
{
|
||
uw_tick += 1;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取系统当前滴答计数
|
||
* @param 无
|
||
* @retval 当前滴答计数
|
||
*/
|
||
uint32_t sys_get_tick(void)
|
||
{
|
||
return uw_tick;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取系统当前毫秒级时间戳。
|
||
* @return {uint32_t} 当前毫秒级时间戳
|
||
* @note: 请注意,这个函数仅用于模拟硬件延时,实际应用中可能需要使用其他时钟源,如RTC或外部时钟。
|
||
*/
|
||
uint32_t sys_millis(void)
|
||
{
|
||
return uw_tick * 10;
|
||
}
|
||
|
||
/**
|
||
* @brief 系统计时器重新开始
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
void sys_millis_reset(void)
|
||
{
|
||
uw_tick = 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 将系统时间戳转换为秒级时间戳。
|
||
* @param {uint32_t} start_time 开始时间戳
|
||
* @return {uint32_t} 秒级时间戳
|
||
* @note: 请注意,这个函数仅用于模拟硬件延时,实际应用中可能需要使用其他时钟源,如RTC或外部时钟。
|
||
*/
|
||
uint32_t sys_to_seconds(uint32_t start_time)
|
||
{
|
||
return (sys_millis() - start_time) / 1000;
|
||
}
|
||
|
||
/**
|
||
* @brief 记录调度器开始时间
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
void scheduler_time_start(void)
|
||
{
|
||
scheduler_start_time = sys_millis();
|
||
}
|
||
|
||
/**
|
||
* @brief 返回调度器运行时间
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
uint32_t scheduler_time_stop(void)
|
||
{
|
||
uint32_t scheduler_end_time = sys_millis() - scheduler_start_time;
|
||
scheduler_occupancy_time += scheduler_end_time;
|
||
return scheduler_end_time;
|
||
}
|
||
|
||
/**
|
||
* @brief 计算任务占用时间百分比
|
||
* @param {uint32_t} run_time 运行时间
|
||
* @return {*} 任务占用时间百分比,单位为%
|
||
* @note
|
||
*/
|
||
uint32_t scheduler_time_occupancy_get(uint32_t run_time)
|
||
{
|
||
float32 percent = 0.0f;
|
||
percent = (float32)(scheduler_occupancy_time) / (float32)run_time;
|
||
scheduler_occupancy_time = 0;
|
||
return (uint32_t)(percent * 100);
|
||
}
|
||
|
||
// 切换到新的时钟配置
|
||
void change_system_clock(clock_config_t *new_config)
|
||
{
|
||
#if CLOCK_CHANGE_ENABLE == TRUE
|
||
// 1. 切换到HSE作为临时时钟源,根据你的硬件配置来调整
|
||
LL_RCC_HSE_Enable();
|
||
while (LL_RCC_HSE_IsReady() != 1)
|
||
{
|
||
}
|
||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSE);
|
||
while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSE)
|
||
{
|
||
}
|
||
|
||
// 2. 关闭PLL
|
||
LL_RCC_PLL_Disable();
|
||
while (LL_RCC_PLL_IsReady() != 0)
|
||
{
|
||
}
|
||
|
||
// 3. 配置新的PLL参数
|
||
// 注意:这里假设HSE作为PLL的时钟源,你需要根据你的硬件配置来调整
|
||
LL_RCC_PLL_ConfigDomain_SYS(new_config->pll_source, new_config->pll_m, new_config->pll_n, new_config->pll_r);
|
||
|
||
// 4. 重新启用PLL
|
||
LL_RCC_PLL_EnableDomain_SYS();
|
||
LL_RCC_PLL_Enable();
|
||
while (LL_RCC_PLL_IsReady() != 1)
|
||
{
|
||
}
|
||
|
||
// 5. 切换回PLL作为系统时钟源
|
||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
|
||
while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
|
||
{
|
||
}
|
||
|
||
// 6. 更新时钟分频器
|
||
LL_RCC_SetAHBPrescaler(new_config->ahb_div);
|
||
LL_RCC_SetAPB1Prescaler(new_config->apb1_div);
|
||
LL_RCC_SetAPB2Prescaler(new_config->apb2_div);
|
||
|
||
// 7. 更新SystemCoreClock变量
|
||
LL_Init1msTick(new_config->sysclk);
|
||
LL_SetSystemCoreClock(new_config->sysclk);
|
||
original_clock_config.sysclk_change = new_config->sysclk;
|
||
|
||
delay_init((SystemCoreClock / 1000000));
|
||
#endif
|
||
}
|
||
|
||
// 恢复到原始时钟配置
|
||
void restore_system_clock(void)
|
||
{
|
||
#if CLOCK_CHANGE_ENABLE == TRUE
|
||
change_system_clock(&original_clock_config);
|
||
#endif
|
||
}
|
||
|
||
// 获取原始时钟配置
|
||
clock_config_t *get_original_clock_config(void)
|
||
{
|
||
return &original_clock_config;
|
||
}
|
||
|
||
// 在系统启动时调用此函数来初始化时钟并保存原始配置
|
||
void system_clock_read(void)
|
||
{
|
||
#if CLOCK_CHANGE_ENABLE == TRUE
|
||
// 保存原始时钟配置
|
||
original_clock_config.pll_source = LL_RCC_PLL_GetMainSource();
|
||
original_clock_config.pll_m = LL_RCC_PLL_GetDivider();
|
||
original_clock_config.pll_n = LL_RCC_PLL_GetN();
|
||
original_clock_config.pll_r = LL_RCC_PLL_GetR();
|
||
|
||
original_clock_config.ahb_div = LL_RCC_GetAHBPrescaler();
|
||
original_clock_config.apb1_div = LL_RCC_GetAPB1Prescaler();
|
||
original_clock_config.apb2_div = LL_RCC_GetAPB2Prescaler();
|
||
original_clock_config.sysclk = SystemCoreClock;
|
||
#endif
|
||
}
|
||
|
||
/**
|
||
* @brief Write a character to a file stream, used for FLASHDB printf
|
||
*
|
||
* Writes the specified character to the given file stream and returns the written character.
|
||
*
|
||
* @param ch The character to be written
|
||
* @param stream Pointer to the file stream
|
||
*
|
||
* @return The written character
|
||
*/
|
||
int fputc(int ch, FILE *stream)
|
||
{
|
||
return ch;
|
||
}
|