#include "sys.h" /** * @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 volatile("wfi"); } /** * @brief 关闭所有中断(但是不包括fault和NMI中断) * @param 无 * @retval 无 */ void sys_intx_disable(void) { __ASM volatile("cpsid i"); } /** * @brief 开启所有中断 * @param 无 * @retval 无 */ void sys_intx_enable(void) { __ASM volatile("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) //{ // __HAL_RCC_PWR_CLK_ENABLE(); /* 使能电源时钟 */ // SET_BIT(PWR->CR, PWR_CR_PDDS); /* 进入待机模式 */ //} /** * @brief 系统软复位 * @param 无 * @retval 无 */ void sys_soft_reset(void) { NVIC_SystemReset(); } #ifdef USE_FULL_ASSERT /** * @brief 当编译提示出错的时候此函数用来报告错误的文件和所在行 * @param file:指向源文件 * line:指向在文件中的行数 * @retval 无 */ void assert_failed(uint8_t* file, uint32_t line) { while (1) { } } #endif //#define IN_SYS //#include "sys.h" //#include "usart.h" //#include "stdio.h" ///** // * @brief 时钟系统配置函数 // * // * @remark SYSCLK = HSE / PLLM * PLLN / PLLR // * //SYSCLK = 8M / 1 * 20 /2 = 80M // * APB 2 高速、APB1 低速。都是可分频的,根据分频系数不一样,可以配出不一样的频率 // * 以外部时钟为8M计算公式:PLLR=8*N/(M*R) // * @param void // * // * @return void // */ uint8_t SystemClock_Config(void) { HAL_StatusTypeDef ret = HAL_OK; RCC_OscInitTypeDef RCC_OscInitStruct={0}; RCC_ClkInitTypeDef RCC_ClkInitStruct={0}; __HAL_RCC_PWR_CLK_ENABLE(); //使能PWR时钟 //Initializes the CPU, AHB and APB busses clocks RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; //打开HSE RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; //打开PLL RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; //PLL时钟源选择HSE RCC_OscInitStruct.PLL.PLLM = 1; // RCC_OscInitStruct.PLL.PLLN = 20; // RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; //80MHz RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) //初始化RCC { return 1; //Error_Handler(1); } //Initializes the CPU, AHB and APB busses clocks RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; //AHB 分频系数为1 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; //APB1 分频系数为4 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; //APB2 分频系数为1 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) { return 1; //Error_Handler(1); } // Configure the main internal regulator output voltage if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK) { return 1; //Error_Handler(1); } return 0; } //void SystemClock_Config(void) //{ // HAL_StatusTypeDef ret = HAL_OK; // RCC_OscInitTypeDef RCC_OscInitStruct = {0}; // RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; // /** Initializes the RCC Oscillators according to the specified parameters // * in the RCC_OscInitTypeDef structure. // */ // RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // RCC_OscInitStruct.HSEState = RCC_HSE_ON; // RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // RCC_OscInitStruct.PLL.PLLM = 1; // RCC_OscInitStruct.PLL.PLLN = 20; // RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) // { // Error_Handler(1); // } // /** Initializes the CPU, AHB and APB buses clocks // */ // RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK // |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; // RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) // { // Error_Handler(1); // } // // ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); // if(ret != HAL_OK) while(1); // /*Configure the main internal regulator output voltage*/ // ret = HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); // if(ret != HAL_OK) while(1); // //HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_SYSCLK, RCC_MCODIV_4); //} ///* //void SystemClock_Config(void) //{ // RCC_OscInitTypeDef RCC_OscInitStruct = {0}; // RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; // // Configure the main internal regulator output voltage // if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK) // { // Error_Handler(1); // //printf("%s:This fake errorr on %d line \r\n", __FUNCTION__,__LINE__); // } // // Initializes the RCC Oscillators according to the specified parametersin the RCC_OscInitTypeDef structure. // RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; // RCC_OscInitStruct.HSIState = RCC_HSI_ON; // RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; // RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; // RCC_OscInitStruct.PLL.PLLM = 1; // RCC_OscInitStruct.PLL.PLLN = 10; // RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) // { // Error_Handler(1); // } // // Initializes the CPU, AHB and APB buses clocks // RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK // |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; // RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;//RCC_HCLK_DIV2;//RCC_HCLK_DIV4; // RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) // { // Error_Handler(1); // } // // HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_SYSCLK, RCC_MCODIV_4); //} //*/ ///** // * @brief THUMB指令不支持汇编内联、 // * 采用如下方法实现执行汇编指令WFI // * // * @param void // * // * @return __asm // */ //__asm void WFI_SET(void) //{ // WFI; //} ///** // * @brief 关闭所有中断(但是不包括fault和NMI中断) // * // * @param void // * // * @return __asm // */ //__asm void INTX_DISABLE(void) //{ // CPSID I // BX LR //} ///** // * @brief 开启所有中断 // * // * @param void // * // * @return __asm // */ //__asm void INTX_ENABLE(void) //{ // CPSIE I // BX LR //} ///** // * @brief 设置栈顶地址 // * // * @param addr 栈顶地址 // * // * @return __asm // */ //__asm void MSR_MSP(u32 addr) //{ // MSR MSP, r0 //set Main Stack value // BX r14 //} ///* //__weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) //{ // uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, // ulSysTickCTRL; // TickType_t xModifiableIdleTime; // // //确保滴答定时器的 Reload(重装载)值不会溢出,也就是不能超过滴答定时器最大计数值。 // //、(1) 参数 xExpectedIdleTime 表示处理器将要在低功耗模式运行的时长(单位为时钟节拍数),这个时间会使用滴答定时器来计时,但是滴答定时器的计数寄存器是 24 位的,因此这个时间值不能超过滴答定时器的最大计数值。xMaximumPossibleSuppressedTicks 是个静态全局变量,在文件 port.c 中有定义,此函数会在函数 vPortSetupTimerInterrupt() 中被重新赋值,代码如下: // if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) //ulTimerCountsForOneTick=(configSYSTICK_CLOCK_HZ/configTICK_RATE_HZ); //xMaximumPossibleSuppressedTicks=portMAX_24_BIT_NUMBER/ulTimerCountsForOneTick; // 经过计算 xMaximumPossibleSuppressedTicks=0xffffff/(168000000/1000)=99,因此进入低功耗模式的最大时长是99个时钟节拍。 // { // xExpectedIdleTime = xMaximumPossibleSuppressedTicks; // } // // //停止滴答定时器。 // portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT; // // //根据参数 xExpectedIdleTime 来计算滴答定时器的重装载值。 // ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + (2) 根据参数 xExpectedIdleTime 来计算滴答定时器的重装载值,因为处理器进入低功耗模式以后的计时是由滴答定时器来完成的。 // ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); // if( ulReloadValue > ulStoppedTimerCompensation ) (3) 从滴答定时器停止运行到把统计得到的低功耗模式运行的这段时间补偿给 FreeRTOS 系统时钟也是需要时间的,这期间也是有程序在运行的。这段程序运行的时间我们要留出来,具体的时间没法去统计。这里只能大概的留出一个时间值,这个时间值由变量 ulStoppedTimerCompensation 来确定,这是一个全局变量。 //#define portMISSED_COUNTS_FACTOR(45UL) // //ulStoppedTimerCompensation=portMISSED_COUNTS_FACTOR/(configCPU_CLOCK_HZ/configSYSTICK_CLOCK_HZ) //通过上面的公式可以得出:ulStoppedTimerCompensation=45/(168000000/168000000)=45。 // { // ulReloadValue -= ulStoppedTimerCompensation; // } // // __disable_irq(); (4) 在执行WFI 前设置寄存器 PRIMASK 的话处理器可以由中断唤醒但是不会处理这些中断,退出低功耗模式以后通过清除寄存器 PRIMASK 来使 ISR 得到执行,其实就是利用 PRIMASK 来延迟 ISR 的执行。函数 __disable_irq(); 用来设置寄存器 PRIMASK,清除寄存器 PRIMASK 使用函数 __enable_irq(); // __dsb( portSY_FULL_READ_WRITE ); // __isb( portSY_FULL_READ_WRITE ); // // //确认是否可以进入低功耗模式 // if( eTaskConfirmSleepModeStatus() == eAbortSleep ) (5) 调用函数 eTaskConfirmSleepModeStatus() 来判断是否可以进入低功耗模式,此函数在文件 tasks.c 中有定义。此函数通过检查是否还有就绪任务来决定处理器能不能进入低功耗模式,如果返回 eAbortSleep 的话就表示不能进入低功耗模式,既然不能进入低功耗模式那么就需要重新恢复滴答定时器的运行。 // { // //不能进入低功耗模式,重新启动滴答定时器 // portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; // portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; // portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; // __enable_irq(); (6) 调用函数__enable_irq();重新打开中断 // } // else (7) 可以进入低功耗模式,完成低功耗相关设置 // { // //可以进入低功耗模式,设置滴答定时器 // portNVIC_SYSTICK_LOAD_REG = ulReloadValue; (8) 进入低功耗模式的时间已经计算出来了,这里将这个值写入到滴答定时器的重装载寄存器中 // portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; // portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; // // xModifiableIdleTime = xExpectedIdleTime; // configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); (9) configPRE_SLEEP_PROCESSING 是个宏,在进入低功耗模式之前可能有一些其他的事情要处理,比如降低系统时钟、关闭外设时钟、关闭板子某些硬件的电源等等,这些操作就可以在这个宏中完成。 // if( xModifiableIdleTime > 0 ) // { // __dsb( portSY_FULL_READ_WRITE ); // __wfi(); (10) 使用 WFI 指令使 STM32F407 进入睡眠模式。 // __isb( portSY_FULL_READ_WRITE ); // } // // //当代码执行到这里的时候说明已经退出了低功耗模式! // configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); (11) 代码执行到这个说明处理器已经退出了低功耗模式,退出低功耗模式以后也可能需要处理一些事情。比如恢复系统时钟,使能外设时钟,打开板子某些硬件的电源等等,这些操作在宏 configPOST_SLEEP_PROCESSING() 中完成。 // // // //停止滴答定时器 // ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG; (12) 读取滴答定时器 CTRL(控制和状态)寄存器。 // portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & // ~portNVIC_SYSTICK_ENABLE_BIT ); // __enable_irq(); (13) 调用函数 __enable_irq();打开中断 // // //判断导致退出低功耗的是由外部中断引起的还是滴答定时器计时时间到引起的 // if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) (14) 判断退出低功耗模式是由滴答定时器中断引起的还是由其他中断引起的,因为这两种原因所对应的系统时钟赔偿值的计算方法不同,这个系统时钟补偿值的单位是时钟节拍。 // { // uint32_t ulCalculatedLoadValue; // ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - // portNVIC_SYSTICK_CURRENT_VALUE_REG ); // // if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || // ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) // { // ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); // } // portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; // ulCompleteTickPeriods = xExpectedIdleTime - 1UL; // } // else //外部中断唤醒的,需要进行时间补偿 // { // ulCompletedSysTickDecrements = ( xExpectedIdleTime * // ulTimerCountsForOneTick ) - // portNVIC_SYSTICK_CURRENT_VALUE_REG; // // ulCompleteTickPeriods = ulCompletedSysTickDecrements // ulTimerCountsForOneTick; // // portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * // ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; // } // // //重新启动滴答定时器,滴答定时器的重装载值设置为正常值。 // portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; // portENTER_CRITICAL(); // { // portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; // vTaskStepTick( ulCompleteTickPeriods ); (15) 调用函数 vTaskStepTick() 补偿系统时钟,函数参数是要补偿的值,此函数在文件 tasks.c 中有如下定义 //void vTaskStepTick(const TickType_t xTicksToJump) //{ // configASSERT((xTickCount+xTickToJump)<=xNextTaskUnblockTime); // xTickCount+=xTicksToJump; // traceINCREASE_TICK_COUNT(xTicksToJump); //} // portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; // } // portEXIT_CRITICAL(); // } //} //*/