(总结)STM32中系统嘀嗒定时器(Systick)的原理和应用

!!!鸣谢轩哥的笔记!!!

一,系统滴答定时器(Systick)

1,基本概念

  • 系统嘀嗒定时器是属于CM4内核中的一个外设,内嵌在NVIC中,所有的基于Cortex-M3/M4内核中的MCU中都包含这个Systick定时器
  • Systick是有ARM公司设计,不是STM公司设计的,Systick跟NVIC捆绑起来的,所以在CM3权威指南或者Cortex M3/M4权威指南中找到Systick的相关信息
  • Systick能产生中断,所以需要编写中断服务函数
  • 2,基本应用

  • 裸机开发:编写专门的延时函数,实现微妙、毫秒级别的延时
  • 操作系统:可以为RTOS实时操作系统的任务调度提供时钟节拍,RTOS的架构是并行的
  • 3,时钟分析

    如果使用Systick定时器进行定时,则需要弄清楚Systick定时器的时钟频率,而定时器是挂载在总线下的,不同的总线的频率是不同的,而总线的频率又是由时钟来提供的,而时钟的提供者又各不相同,所以必须要了解各时钟源的区别

    3.1  HSI  高速内部时钟

    3.2  HSE  高速外部时钟

      (笔试题常考)
    3.3  LSE  低速外部时钟

      (笔试题常考)
    3.4  LEI  低速内部时钟

    3.5  PLL 倍频锁相环

    注意:

    1. 通过阅读ST公司提供的函数库的帮助手册,可以知道ST公司默认使用的高速外部时钟的频率是25MHZ,并且PLL_M = 25,PLL_N = 336,PLL_P = 2, 芯片主频 = HSE / M * N / P = 168M
    2. PLL可以理解为是一个加工器,负责放大或者缩小频率,其本身是没有时钟频率的
  • 4,时钟树分析

  • 4.1  LSI  低速内部时钟(常用于看门狗)

    4.2  LSE  低速外部时钟(常用于RTC实时时钟,如日历)

    4.3 HSI  高速内部时钟(低功耗时使用,一般不使用)

    4.4 HSE  高速外部时钟

    巧记:M(8)  N(336)  P(2)

    ———————————————————————————————————————————

    注意:如果使用的是STM32F4xx系列,需要修改倍频锁相环参数

    开发板默认使用的HSE时钟源的频率是8MHZ,为了能够计算出168MHZ的芯片主频,所以需要修改PLL_M的值,所以需要修改system_stm32f4xx.c文件,并且还需要修改stm32f4xx.h头文件中HSE_VALUE宏定义的值。

  • 修改system_stm32f4xx.c源文件的316行,必须把宏定义PLL_M的值从25修改为8 !!
  • 修改stm32f4xx.h头文件中HSE_VALUE宏定义的值,修改文件的123行,把25修改为8 
  • 这两个位置修改完成后,PLL倍频锁相环的参数才是正确的,所以计算出来的频率就是正确的。

    ———————————————————————————————————————————

    5,时钟选择

    通过M3内核文档可以知道Systick定时器有2个时钟源,一个是内部时钟(FCLK,指的是MCU的自由运行时钟,就是168MHZ),一个是外部时钟(STCLK,目前STM32F407ZET6这颗MCU是把AHB总线的时钟频率进行8分频,就是168MHZ /8 = 21MHZ)

    5.1   内部时钟

    如果选择使用内部时钟(168MHZ)作为系统嘀嗒定时器的时钟源,则嘀嗒定时器的计数周期:1000000us生成168000000个脉冲,意味着 1us可以生成168个脉冲,所以计数周期等于1/168us。

    5.2   外部时钟

    如果选择使用外部时钟(21MHZ)作为系统嘀嗒定时器的时钟源,则嘀嗒定时器的计数周期:1000000us生成21000000个脉冲,意味着 1us可以生成21个脉冲,所以计数周期等于1/21us。

    二,Systick的控制方式

    因为Systick 是内嵌在NVIC中的,所以ST公司并没有编写对应的示例代码,所以需要参考内核文档(Cortex M3/M4权威指南

    三,如何使用Systick定时器

    1,中断定时方式

    (注意必须采用168MH作为时钟源)

    代码实现:

    /*
      ******************************************************************************
      * @file    main.c 
      * @author  
      * @version V1.0
      * @date    2024/09/25
      * @brief   学习内核中的系统滴答定时器的定时方式,本案例采用的是Systick的中断
    						 方式实现延时,采用该方式必须使用168MHZ作为Systick的时钟源,该方式
    						 可以实现周期性的延时,会周期性的触发Systick异常
    					   
      ******************************************************************************
    */
    
    #include "stm32f4xx.h" //必须包含
    
    
    /* Private typedef   用于记录用户自定义的一些数据类型的别名-------------------*/
    
    
    /* Private define    用于记录用户自定义的类型,比如结构体、共用体、枚举-------*/
    
    
    /* Private macro     用于记录用户自定义的宏定义-------------------------------*/
    
    
    /* Private variables 用于记录用户自定义的全局变量-----------------------------*/
    volatile int Cnt = 0; 
    
    /* Private function prototypes 用于记录用户自定义的函数声明-------------------*/
    
    
    /* Private functions 用于记录用户自定义的函数原型-----------------------------*/
    
    
    /**
      * @brief  LED的初始化
      * @param  None
      * @retval None
      */
    void LED_Init(void)
    {
    	GPIO_InitTypeDef  GPIO_InitStructure;
    	
    	//打开外设的时钟   LED --- PF9
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
    	
    	//配置引脚的参数
      GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_9 | GPIO_Pin_10;
      GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_OUT;
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
      GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_NOPULL;
    	
    	//初始化GPIO端口
    	GPIO_Init(GPIOF, &GPIO_InitStructure);
    }
    
    void delay(uint32_t nms)
    {
    	Cnt = nms; //把要延时的时间放入变量
    	while(Cnt != 0);
    }
    
    
    
    /**
      * @brief  程序的入口
      * @param  None
      * @retval None
      */
    int main(void)
    {
    	//硬件的初始化
    	LED_Init();
    	
    	SysTick_Config(168*1000); //中断的触发频率是1KHZ
    	
      while (1)
      {	
    		GPIO_ToggleBits(GPIOF,GPIO_Pin_9);
    		delay(1000);						//延时单位是ms
      }
    }
        
    //Systick中断服务函数
    void SysTick_Handler(void)
    {
    	if(Cnt != 0)
    	{
    		Cnt--;
    	}
    }
    
    /********************** (C) COPYRIGHT Your Name xxxx@126.com***END OF FILE****/
    

    注意:

  • 当调用了SysTick_Config(168*1000) 意味着每隔1ms自动触发定时器中断
  • 在定时器中断服务函数中对全局变量进行递减,对全局变量进行
  • 2,非中断定时方式

    代码实现:

    /**
      * @brief  延时微秒
      * @param  
    						@nus :待延时的微秒  注意:不能超过798915us
      * @retval None
      */
    void delay_us(uint32_t nus)
    {
    	SysTick->CTRL = 0; 		// 关闭定时器
    	SysTick->LOAD = nus * 21 - 1; 		// 设置重载值  nus * 21 - 1 
    	SysTick->VAL 	= 0; 		// 清除当前值
    	SysTick->CTRL = 1; 		// 打开定时器并且使用参考时钟 168MHZ/8 = 21MHZ
    	while ((SysTick->CTRL & 0x00010000)==0);// 等待计数值递减到0
    	SysTick->CTRL = 0; 		// 关闭定时器
    }
    
    
    /**
      * @brief  延时毫秒
      * @param  
    						@nms :待延时的毫秒  
      * @retval None
      */
    void delay_ms(uint32_t nms)
    {
    	while(nms--)
    	{
    		SysTick->CTRL = 0; 		// 关闭定时器
    		SysTick->LOAD = 21*1000 - 1; 		// 设置重载值  nus * 21 - 1 
    		SysTick->VAL 	= 0; 		// 清除当前值
    		SysTick->CTRL = 1; 		// 打开定时器并且使用参考时钟 168MHZ/8 = 21MHZ
    		while ((SysTick->CTRL & 0x00010000)==0);// 等待计数值递减到0
    		SysTick->CTRL = 0; 		// 关闭定时器
    	}
    }
    
    /**
      * @brief  程序的入口
      * @param  None
      * @retval None
      */
    int main(void)
    {
    	//硬件的初始化
    	LED_Init();
    	
    	
    	
      while (1)
      {	
    		GPIO_ToggleBits(GPIOF,GPIO_Pin_9);
    		delay_ms(1000);    //每隔1s,LED的电平翻转一次
      }
    }

    步骤总结:

    1. 关闭定时器
    2. 设置重载值
    3. 清空当前值
    4. 打开定时器
    5. 等待计数完成
    6. 关闭定时器

    作者:100 Miles485

    物联沃分享整理
    物联沃-IOTWORD物联网 » (总结)STM32中系统嘀嗒定时器(Systick)的原理和应用

    发表回复