赞
踩
Systick是向下递减的,而51单片机是向上计数,每来一个时间脉冲就计数加一,而Systick是每来一个就向下计数加一。理论上可以计2^24个值,减为0的时候产生中断,恢复成某个设定的的数,然后重新向下递减。
Systick的作用:
下面结合程序看一下Systick初始化的流程:
看一下HAL_Init();
函数的实现:
HAL_StatusTypeDef HAL_Init(void) { HAL_StatusTypeDef status = HAL_OK; /* Configure Flash prefetch, Instruction cache, Data cache */ /* Default configuration at reset is: */ /* - Prefetch disabled */ /* - Instruction cache enabled */ /* - Data cache enabled */ #if (INSTRUCTION_CACHE_ENABLE == 0U) __HAL_FLASH_INSTRUCTION_CACHE_DISABLE(); #endif /* INSTRUCTION_CACHE_ENABLE */ #if (DATA_CACHE_ENABLE == 0U) __HAL_FLASH_DATA_CACHE_DISABLE(); #endif /* DATA_CACHE_ENABLE */ #if (PREFETCH_ENABLE != 0U) __HAL_FLASH_PREFETCH_BUFFER_ENABLE(); #endif /* PREFETCH_ENABLE */ //上面这些宏定义和if,endif都是一些初始化的东西,不用管 /* Set Interrupt Group Priority */ HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); //上面这句是配置中断优先级的组 /* Use SysTick as time base source and configure 1ms tick (default clock after Reset is HSI) */ if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) { status = HAL_ERROR; } else { /* Init the low level hardware */ HAL_MspInit(); } /* Return function status */ return status; //上面这段就是将Systick配置成一个1ms的时间基准,并且将上电默认的时钟设置为HSI。 }
点开HAL_InitTick()
这个函数可以看到:
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) { HAL_StatusTypeDef status = HAL_OK; if (uwTickFreq != 0U) { /* Configure the SysTick to have interrupt in 1ms time basis*/ //这个函数最主要的部分就是下面这句,设置了一个溢出的标志位 if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) == 0U) { /* Configure the SysTick IRQ priority */ if (TickPriority < (1UL << __NVIC_PRIO_BITS)) { HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U); uwTickPrio = TickPriority; } else { status = HAL_ERROR; } } else { status = HAL_ERROR; } } else { status = HAL_ERROR; } /* Return function status */ return status; }
再打开HAL_SYSTICK_Config()
这个函数中的SysTick_Config()
函数:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{//可以看到就是很重要的一步:配置了重装载值
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL); /* Reload value impossible */
}
SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0UL); /* Function successful */
}
最后就是要明白一点:对于Systick来说,一定要知道的就是uwTick这个变量的意义:就是每过1ms他就会自加1。
uwTick这个变量在程序中是定义为__IO uint32_t uwTick;
,是一个32位的无符号数,所以他一共可以计
2
32
2^{32}
232这么多毫秒,差不多是49天左右,所以不用担心会有溢出的问题,即使溢出了,他也会自动归零。
HAL_Delay延时会阻塞程序,使其他程序实时性下降。
除了HAl_Delay延时函数之外,可以将Systick用于程序调度。
我们以LED灯的程序为例写出如下调度代码:
//由于uwTick的类型是32位无符号整型,所以我们自定义的ledTick也定义为这样的类型:
__IO uint32_t ledTick =0;
//下面这个变量的定义是上一篇文章中提到的对led灯哪个亮哪个不亮的控制,初始化为0xff就是使所有灯都亮
u8 led_ctrl=0xff;
void LED_Process(void)
{
//由于uwTick在上电之后就会每1ms加一,而ledTick一开始初始化为0,所以uwTick-ledTick的值就是uwTick本身,当uwTick大于500,即程序上电500毫秒之后,执行后面的代码,否则就return,也就是退出这个函数
if(uwTick-ledTick<500)return;
// 把uwTick的值赋给ledTick,为了下次进入这个函数还是500毫秒之后执行该程序(因为uwTick的值是一直递增的,所以想要控制uwTick与ledTick的差值始终为500就需要每进一次这个函数更新一次ledTick)
ledTick=uwTick;
//控制led灯的亮灭
LED_Control(led_ctrl);
//每进一次这个函数就把led_ctrl取反,相当于每500毫秒,灯整体亮或者灭
led_ctrl=~led_ctrl;
}
最终的主函数如下:
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * <h2><center>© Copyright (c) 2021 STMicroelectronics. * All rights reserved.</center></h2> * * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "gpio.h" #include "led.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ //Led执行程序 __IO uint32_t ledTick =0; u8 led_ctrl=0xff; void LED_Process(void) { if(uwTick-ledTick<500)return; ledTick=uwTick; LED_Control(led_ctrl); led_ctrl=~led_ctrl; } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); /* USER CODE BEGIN 2 */ LCD_Init(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ LCD_Clear(Blue); LCD_SetBackColor(Blue); LCD_SetTextColor(White); LCD_DisplayStringLine(Line0, (uint8_t *)" "); LCD_DisplayStringLine(Line1, (uint8_t *)" "); LCD_DisplayStringLine(Line2, (uint8_t *)" LCD Test "); LCD_DisplayStringLine(Line3, (uint8_t *)" "); LCD_DisplayStringLine(Line4, (uint8_t *)" "); LCD_SetBackColor(White); LCD_SetTextColor(Blue); LCD_DisplayStringLine(Line5, (uint8_t *)" "); LCD_DisplayStringLine(Line6, (uint8_t *)" HAL LIB "); LCD_DisplayStringLine(Line7, (uint8_t *)" "); LCD_DisplayStringLine(Line8, (uint8_t *)" @80 "); LCD_DisplayStringLine(Line9, (uint8_t *)" "); while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ LED_Process(); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the CPU, AHB and APB busses clocks */ 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 = RCC_PLLM_DIV2; RCC_OscInitStruct.PLL.PLLN = 20; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** 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; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
就可以实现500毫秒的间隔闪烁。这样做的好处就是不堵塞程序,而不是在while里面用delay。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。