STM32F407的系统定时器

文章目录

  • 系统定时器SysTick
    • 滴答定时器寄存器
    • STK_CTRL 控制寄存器
    • STK_LOAD 重载寄存器
    • STK_VAL 当前值寄存器
    • STK_CALRB 校准值寄存器
  • 非系统初始化 Systick 定时器
    • SysTick_Init
      • SysTick_CLKSourceConfig
    • delay_us寄存器
    • delay_us库函数
    • delay_xms短时
    • delay_ms长时
    • SysTick_Config
  • 系统RTOS延时

系统定时器SysTick

CM4 内核的处理和 CM3 一样,内部都包含了一个SysTick定时器。两者没有区别。其详细介绍,请参阅
《STM32F3与F4系列Cortex M4内核编程手册》第230页。

  • 系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。因为所有的 CM3 芯片都带有这个定时器,软件在不同 CM3 器件间的移植工作得以化简。

  • 系统定时器是一个 24bit 的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK 等于 72M。

    • 该定时器的时钟源可以是内部时钟,或者是外部时钟。不过,STCLK 的具体来源则由芯片设计者决定
  • 当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。

    • SysTick 设定初值并使能后,每经过 1 个系统时钟周期,计数值就减 1。计数到 0 时,SysTick 计数器自动重装初值并继续计数,同时内部的 COUNT FLAG 标志会置位,触发中断 (如果中断使能情况下)。
  • 只要不把它在 SysTick 控制及状态寄存器中的使能位清除,就永不停息。
    在这里插入图片描述在M4内核编程手册中这样说道:SysTick是一个24位系统定时器,它将重加载值递减到零。再在下一个时钟沿将该值重新装载到STK_LOAD寄存器中,然后根据随后的时钟进行递减。

滴答定时器寄存器

Systick 部分内容属于 NVIC 控制部分,一共有 4 个寄存器,名称和地址分别是:

STK_CTRL       0xE000E010  --  控制寄存器	
//貌似这个才是控制寄存器
STK_LOAD     0xE000E014  --  重载寄存器	
STK_VAL      0xE000E018  --  当前值寄存器	
STK_CALIB    0xE000E01C  --  校准值寄存器

在这里插入图片描述

STK_CTRL 控制寄存器

在这里插入图片描述
寄存器内有 4 个位具有意义

  • 第 0 位:ENABLE,Systick 使能位(0:关闭 Systick 功能;1:开启 Systick 功能)
  • 第 1 位:TICKINT,Systick 中断使能位(0:关闭 Systick 中断;1:开启 Systick 中断)
  • 第 2 位:CLK SOURCE,Systick 时钟源选择(0:使用 HCLK/8 作为 Systick 时钟;1:使用 HCLK 作为 Systick 时钟)
  • 第 16 位:COUNT FLAG,Systick 计数比较标志,当计数到0时,置1;定时器开始重新计数时(CLK_LOAD重新写入数值)自动清零。

在这里插入图片描述

STK_LOAD 重载寄存器

在这里插入图片描述

Systick 是一个递减的定时器,当定时器递减至0 时,重载寄存器中的值就会被重装载,继续开始递减。STK_LOAD 重载寄存器是个24 位的寄存器最大计数0xFFFFFF。

STK_VAL 当前值寄存器

在这里插入图片描述

24 位的寄存器,读取时返回当前倒计数的值,写它则使之清零,同时还会清除在 SysTick 控制及状态寄存器中的 COUNTFLAG 标志。
在这里插入图片描述

STK_CALRB 校准值寄存器

SysTick calibration value register
在这里插入图片描述

  • 位31 NOREF:= 1 没有外部参考时钟(STCLK 不可用),= 0 外部参考时钟可用。
  • 位30 SKEW:= 1 校准值不是准确的1ms,= 0校准值是准确的1ms
  • 不常用

非系统初始化 Systick 定时器

在这里插入图片描述

SysTick_Init

void SysTick_Init(u8 SYSCLK);
SysTick_Init(72);

//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void SysTick_Init(u8 SYSCLK)
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
	fac_us=SYSCLK/8;					
	fac_ms=(u16)fac_us*1000;				   
}								    
 

系统时钟是72/8M,计数一次时间1/9000000秒,换算成us就是1/9us,则计数72/8次也就是9次就是1us。

SysTick_CLKSourceConfig

设置SysTick的时钟源。可选项如下:

#define SysTick_CLKSource_HCLK_Div8    ((uint32_t)0xFFFFFFFB)
#define SysTick_CLKSource_HCLK         ((uint32_t)0x00000004)
#define IS_SYSTICK_CLK_SOURCE(SOURCE) (((SOURCE) == SysTick_CLKSource_HCLK) || \
                                       ((SOURCE) == SysTick_CLKSource_HCLK_Div8))

HCLK的介绍

delay_us寄存器

void delay_us(u32 nus)
{	 
	u32 temp;
	SysTick->LOAD = 9*nus;
	SysTick->VAL=0X00;//清空计数器
	SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
	do
	{
		temp=SysTick->CTRL;//读取当前倒计数值
	}while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达
	//使能且没有计数到0就一直循环
	SysTick->CTRL=0x00; //关闭计数器
	SysTick->VAL =0X00; //清空计数器
}

9*nus :假设外设频率为 9M,也就是经过 8 分频,那么计数 9 次是 1us,乘以 9 的意义就是参数的时间对应的次数,也就是重装载值

  • 这里看一下判断条件,第一位是使能,16位是计数到0。
    第 16 位:COUNT FLAG,Systick 计数比较标志,当计数到0时,置1;定时器开始重新计数时(CLK_LOAD重新写入数值)自动清零。

总结一下过程

  • 先把要延时的 us 数换算成 SysTick 的时钟数
  • 写入 LOAD 寄存器
  • 清空当前寄存器 VAL 的内容
  • 开启倒数功能。
  • 等到倒数结束,即延时了 nus。
  • 最后关闭 SysTick,清空 VAL 的值

注意:

  • nus 的值,不能太大,必须保证 nus<=(2^24)/fac_us,否则将导致延时时间不准确
  • temp&0x01,这一句是用来判断 systick 定时器是否还处于开启状态,可以防止 systick 被意外关闭导致的死循环。

delay_us库函数

//延时nus
//注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; 				//时间加载	  		 
	SysTick->VAL=0x00;        				//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 	 
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));	//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
	SysTick->VAL =0X00;       				//清空计数器 
}

delay_xms短时


//延时nms
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对168M条件下,nms<=798ms 
void delay_xms(u16 nms)
{	 		  	  
	u32 temp;		   
	SysTick->LOAD=(u32)nms*fac_ms;			//时间加载(SysTick->LOAD为24bit)
	SysTick->VAL =0x00;           			//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;          //开始倒数 
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));	//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;       //关闭计数器
	SysTick->VAL =0X00;     		  		//清空计数器	  	    
} 

delay_ms长时


//延时nms 
//nms:0~65535
void delay_ms(u16 nms)
{	 	 
	u8 repeat=nms/540;						//这里用540,是考虑到某些客户可能超频使用,
											//比如超频到248M的时候,delay_xms最大只能延时541ms左右了
	u16 remain=nms%540;
	while(repeat)
	{
		delay_xms(540);
		repeat--;
	}
	if(remain)delay_xms(remain);
} 

SysTick_Config

设置重装载值。该函数的内部就是个:SysTick->LOAD = 9*nus;
SysTick_Config 的参数是一个时钟次数,意思就是我要多少个1/fosc 时间后中断一下。

static void BSP_CoreClockInit(void)
{ 
	SysTick_Config(SystemCoreClock / 100);      //10ms
	//SysTick_Config(720000);    				//10ms
}

SystemCoreClock / 100 代表的是时钟次数,即放入重装载寄存器中的值,每计时一个数需要 1/72000000s 的时间,那么计SystemCoreClock / 100 (SystemCoreClock 为 72M)个数的时间就是 10ms,通过在中断中设置标志位达到 systick 定时中断的功能。
函数实现

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk)  return (1);      /* Reload value impossible */

  SysTick->LOAD  = ticks - 1;                                  /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0;                                          /* 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 (0);                                                  /* Function successful */
}

在外设的中断优先级设置里,外设的硬件编号都是大于等于零的,内核外设的编号都小于零,相当于一个IP。

  • __NVIC_PRIO_BITS=4,因为F4使用了四位。
  • 中断处理函数IP:
typedef enum IRQn
{
/******  Cortex-M4 Processor Exceptions Numbers ****************************************************************/
  NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable Interrupt                                          */
  MemoryManagement_IRQn       = -12,    /*!< 4 Cortex-M4 Memory Management Interrupt                           */
  BusFault_IRQn               = -11,    /*!< 5 Cortex-M4 Bus Fault Interrupt                                   */
  UsageFault_IRQn             = -10,    /*!< 6 Cortex-M4 Usage Fault Interrupt                                 */
  SVCall_IRQn                 = -5,     /*!< 11 Cortex-M4 SV Call Interrupt                                    */
  DebugMonitor_IRQn           = -4,     /*!< 12 Cortex-M4 Debug Monitor Interrupt                              */
  PendSV_IRQn                 = -2,     /*!< 14 Cortex-M4 Pend SV Interrupt                                    */
  SysTick_IRQn                = -1,     /*!< 15 Cortex-M4 System Tick Interrupt                                */
/******  STM32 specific Interrupt Numbers **********************************************************************/
  WWDG_IRQn                   = 0,      /*!< Window WatchDog Interrupt                                         */
  PVD_IRQn                    = 1,      /*!< PVD through EXTI Line detection Interrupt                         */
  TAMP_STAMP_IRQn             = 2,      /*!< Tamper and TimeStamp interrupts through the EXTI line             */
  RTC_WKUP_IRQn               = 3,      /*!< RTC Wakeup interrupt through the EXTI line                        */
  FLASH_IRQn                  = 4,      /*!< FLASH global Interrupt                                            */
  RCC_IRQn                    = 5,      /*!< RCC global Interrupt                                              */
  EXTI0_IRQn                  = 6,      /*!< EXTI Line0 Interrupt                                              */
  EXTI1_IRQn                  = 7,      /*!< EXTI Line1 Interrupt                                              */
  EXTI2_IRQn                  = 8,      /*!< EXTI Line2 Interrupt                                              */
  EXTI3_IRQn                  = 9,      /*!< EXTI Line3 Interrupt                                              */
  EXTI4_IRQn                  = 10,     /*!< EXTI Line4 Interrupt                                              */
  DMA1_Stream0_IRQn           = 11,     /*!< DMA1 Stream 0 global Interrupt                                    */
  DMA1_Stream1_IRQn           = 12,     /*!< DMA1 Stream 1 global Interrupt                                    */
  DMA1_Stream2_IRQn           = 13,     /*!< DMA1 Stream 2 global Interrupt                                    */
  DMA1_Stream3_IRQn           = 14,     /*!< DMA1 Stream 3 global Interrupt                                    */
  DMA1_Stream4_IRQn           = 15,     /*!< DMA1 Stream 4 global Interrupt                                    */
  DMA1_Stream5_IRQn           = 16,     /*!< DMA1 Stream 5 global Interrupt                                    */
  DMA1_Stream6_IRQn           = 17,     /*!< DMA1 Stream 6 global Interrupt                                    */
  ADC_IRQn                    = 18,     /*!< ADC1, ADC2 and ADC3 global Interrupts   

内核外设和普通外设的中断优先级比较,虽然二者不是在同一个寄存器中,但是在比较时,也是把内核外设的优先级寄存器配置按照普通的解读。

系统RTOS延时

//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
	u32 reload;
 	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);  //168M,8分频 21M
	fac_us=SYSCLK/8;						//不论是否使用OS,fac_us都需要使用21M
	//168M也就是168次/us,对于滴答就是21次/us,所以计时21次就是1us,单位就是fac_us
	reload=SYSCLK/8;						//每秒钟的计数次数 单位为M	   
	reload*=1000000/delay_ostickspersec;	//根据delay_ostickspersec设定溢出时间
											//reload为24位寄存器,最大值:16777216,在168M下,约合0.7989s左右	
	fac_ms=1000/delay_ostickspersec;		//代表OS可以延时的最少单位	   
	SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;   	//开启SYSTICK中断
	SysTick->LOAD=reload; 					//每1/delay_ostickspersec秒中断一次	
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; 	//开启SYSTICK    

}	

这里需要注意的是:SysTick 的时钟源自 HCLK 的 8 分频,假设我们外部晶振为 8M,
然后倍频到 168M,那么 SysTick 的时钟即为 21Mhz,也就是 SysTick 的计数器 VAL 每减 1,就
代 表 时 间 过 了 1/21us 。

fac_us=SYSCLK/8; ; 这 句 话 就 是 计 算 在SystemCoreClock 时 钟 频 率 下 延 时 1us 需 要 多 少 个 SysTick 时 钟 周 期。21M的时钟,21次是1us,1次就是1/21us。sysClk就是168。fac_us就是168/8次。

Systick 的时钟来自系统时钟 8 分频,正因为如此,系统时钟如果不是 8 的倍数(不能被 8 整除),则会导致延时函数不准确,这也是我们推荐外部时钟选择 8M 的原因

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/114839.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Firefox修改缓存目录的方法

打开Firefox&#xff0c;在地址栏输入“about:config” 查找是否有 browser.cache.disk.parent_directory&#xff0c;如果没有就新建一个同名的字符串&#xff0c;然后修改值为你要存放Firefox浏览器缓存的目录地址&#xff08;E:\FirefoxCacheFiles&#xff09; 然后重新…

在python中加载tensorflow-probability模块和numpy模块

目录 操作步骤&#xff1a; 注意&#xff1a; 问题&#xff1a; 解决办法&#xff1a; 操作步骤&#xff1a; 在虚拟环境的文件夹中&#xff0c;找到Scripts文件夹&#xff0c;点击进去&#xff0c;找到地址栏&#xff0c;在地址栏中输入cmd&#xff0c;进入如下界面。 输…

详解Java经典数据结构——HashMap

Java 的 HashMap 是一个常用的基于哈希表的数据结构&#xff0c;它实现了 Map 接口&#xff0c;可以存储键值对。下面我们进行详细介绍&#xff1a; 基本结构&#xff1a;HashMap 底层是基于哈希表来实现的&#xff0c;每次插入一个键值对时&#xff0c;会先对该键进行 Hash 运…

思维训练3

题目描述1 Problem - A - Codeforces 题目分析 样例1解释&#xff1a; 对于此题&#xff0c;我们采用贪心的想法&#xff0c;从1到n块数越少越好&#xff0c;故刚好符合最少的块数即可&#xff0c;由于第1块与第n块是我们必须要走的路&#xff0c;所以我们可以根据这两块砖的…

C++--二叉搜索树初阶

前言&#xff1a;二叉搜索树是一种常用的数据结构&#xff0c;支持快速的查找、插入、删除操作&#xff0c;C中map和set的特性也是以二叉搜索树作为铺垫来实现的&#xff0c;而二叉搜索树也是一种树形结构&#xff0c;所以&#xff0c;在学习map和set之前&#xff0c;我们先来学…

鸿运主动安全云平台任意文件下载漏洞复习

简介 深圳市强鸿电子有限公司鸿运主动安全监控云平台网页存在任意文件下载漏洞&#xff0c;攻击者可通过此漏洞下载网站配置文件等获得登录账号密码 漏洞复现 FOFA语法&#xff1a;body"./open/webApi.html" 获取网站数据库配置文件 POC&#xff1a;/808gps/Mobile…

Android Studio中配置Git

安装Git 在安装Android Studio之前&#xff0c;需要先安装Git。可以从Git官网下载并安装Git&#xff1a;https://git-scm.com/downloads 在Android Studio中配置Git 在Android Studio中&#xff0c;依次点击“File” -> “Settings”&#xff0c;在弹出的窗口中选择“Ver…

vue学习part01

02_Vue简介_哔哩哔哩_bilibili Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org) 1.简介 2.常用用法 新项目一般vue3&#xff0c;老项目vue2 3.vue两种风格&#xff1a;选项式api&#xff08;vue2&#xff09;和组合式api&#xff08;vue3&#xff09; 两种方式实现累…

财务数字化转型的切入点是什么?_光点科技

随着科技的不断进步&#xff0c;数字化转型已经成为各个行业追求的目标&#xff0c;财务领域也不例外。那么&#xff0c;财务数字化转型的切入点在哪里呢&#xff1f;如何确保转型的成功进行&#xff1f; 数据整合与管理 财务数据的准确性与及时性是财务管理的基石。数字化转型…

桥和割点,以及图的遍历树

目录 什么是桥 寻找桥的算法 代码实现 什么是割点 ​寻找割点的算法 代码实现 什么是桥 寻找桥的算法 代码实现 import java.util.ArrayList;public class FindBridges {private Graph G;private boolean[] visited;private int ord[];private int low[];private int cnt…

在基于亚马逊云科技的湖仓一体架构上构建数据血缘的探索和实践

背景介绍 随着大数据技术的进步&#xff0c;企业和组织越来越依赖数据驱动的决策。数据的质量、来源及其流动性因此显得非常关键。数据血缘分析为我们提供了一种追踪数据从起点到终点的方法&#xff0c;有助于理解数据如何被转换和消费&#xff0c;同时对数据治理和合规性起到关…

阿里云的OSS云存储的基本使用

阿里云官网&#xff1a;阿里云-计算&#xff0c;为了无法计算的价值 通过阿里云官网&#xff0c;登录进入用户的界面&#xff0c;在搜索框中输入OSS&#xff0c;然后进入阿里云的对象存储OSS的控制台。&#xff08;未开通的开通即可&#xff09; 创建 Bucket 点击【Bucket 列…

OpenShift - 利用容器的特权配置实现对OpenShift攻击

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在 OpenShift 4.13 的环境中验证 本文是《容器安全 - 利用容器的特权配置实现对Kubernetes攻击》的后续篇&#xff0c;来介绍 在 OpenShift 环境中的容器特权配置和攻击过程和 Kubernetes 环境的差异。 文…

JVM——类的生命周期(加载阶段,连接阶段,初始化阶段)

目录 1.加载阶段2.连接阶段1.验证2.准备3.解析 3.初始化阶段4.总结 类的生命周期 1.加载阶段 ⚫ 1、加载(Loading)阶段第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息。 程序员可以使用Java代码拓展的不同的渠道。 ⚫ 2、类加载器在加载完类…

14.Eclipse全局查找字符,类,方法快捷键

eclipse开发中&#xff0c;查找会是一个经常用到的功能所以总结一下 1&#xff0c;查找一个类 Shift Ctrl h 或者 Shift Ctrl r 这种方式能快速的定位接口&#xff0c;类还有注解在那个包里面 2.综合查找 Ctrl H 这是一种综合查找 &#xff0c;可以用来查找 一个方法的…

selenium爬虫——以爬取澎湃新闻某搜索结果为例

文章目录 selenium爬虫——以爬取澎湃新闻某搜索结果为例前言需要导入的包需要避雷的点webdriver的版本要与浏览器一致如果使用爬虫打开了新网页&#xff0c;要记得跳转XPath和selector都可以直接复制爬取多网页时记得try打入word时调整字体的问题 完整程序扩展爬取效果 seleni…

【蓝桥杯】2023省赛H题

考察知识点&#xff1a;双向链表&#xff0c;小根堆 完整代码在文章末尾 题目 【问题描述】 给定一个长度为 N 的整数数列: A1,A2,...,AN。你要重复以下操作 K 次 :…

利用云计算和微服务架构开发可扩展的同城外卖APP

如今&#xff0c;同城外卖APP已经成为了人们点餐的主要方式之一。然而&#xff0c;要构建一款成功的同城外卖APP&#xff0c;不仅需要满足用户的需求&#xff0c;还需要具备可扩展性&#xff0c;以适应快速增长的用户和订单量。 一、了解同城外卖APP的需求 在着手开发同城外卖…

Doris:StreamLoad导入数据

目录 1.基本原理 2.支持数据格式 3.StreamLoad语法 3.1.请求参数 3.2.返回参数 4.StreamLoad实践 4.1.使用 curl命令 4.2.使用Java代码 Stream load 是一个同步的导入方式&#xff0c;用户通过发送 HTTP 协议发送请求将本地文件或数据流导入到 Doris 中。Stream load 主…