/* * @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 new_config->last_sysclk = SystemCoreClock; // 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); 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; }