STM32的systick配置好不需要 再使能nvic就可以使用吗?

滴答定时器整理(STM32F103)。用了一段时间的滴答定时器,突然忘记其中配置方法,重新翻阅手册及博文,在此记录备忘。


 
 
 
 
 
 
  • STK_CSR控制寄存器:寄存器低4位含义
  • 第3位:COUNTFLAG,Systick计数比较标志,如果在上次读取本寄存器后,SysTick 已经数到了0,则该位为1。如果读取该位,该位将自动清零
 
 
  • Systick是一个递减的定时器,当定时器递减至0时,重载寄存器中的值就会被重装载,继续开始递减。STK_LOAD 重载寄存器是个24位的寄存器最大计数0xFFFFFF。

  •  
  • 也是个24位的寄存器,读取时返回当前倒计数的值,写它则使之清零,同时还会清除在SysTick 控制及状态寄存器中的COUNTFLAG 标志。

  •  
    • 位31 NOREF :1=没有外部参考时钟(STCLK 不可用)0=外部参考时钟可用
  •  
     
     
     
     
     
     
     
     
     
    
      

本章教程为大家讲解制作一个STM32H7的例子所需的最基本API函数,对于一些常用的API函数,一定要熟练掌握这些函数都是实现了什么功能,不常用的函数有个了解即可,用到的时候再去学。


16.1 初学者重要提示

  •   对于一些常用的函数,大家一定要熟练的掌握都实现了什么功能,比如HAL_Init,HAL_RCC_OscConfig,HAL_RCC_ClockConfig等。最好的办法是把这些函数的源码读一遍。

这里我们通过一个简单的初始化流程来了解STM32H7的工程模板所必备的库文件和API:

  第1步:系统上电复位,进入启动文件startup_stm32h743xx.s,在这个文件里面执行复位中断服务程序。

  •   之后是调用编译器封装好的函数,比如用于MDK的启动文件是调用__main,最终进入到main函数。

  第2步:进入到main函数就可以开始用户应用程序编程了。在这个函数里面要做几个重要的初始化,依次是:

前面的两步完成后,就可以开始做用户需要的按键、串口等方面的初始化和应用代码的实现了。这里把我们需要学习的几个库文件整理出来,依次有:

这个文件比较杂,像基准电压大小配置,EXTI配置,IO补偿配置等都在这个文件里面设置。学习这个文件注意事项:

  •   HAL库中各个外设驱动里面的延迟实现是基于此文件提供的时间基准,而这个时间基准既可以使用滴答定时器实现也可以使用通用的定时器实现,默认情况下是用的滴答定时器。
  • 如果在中断服务程序里面调用延迟函数HAL_Delay要特别注意,因为这个函数的时间基准是基于滴答定时器或者其他通用定时器实现,实现方式是滴答定时器或者其他通用定时器里面做了个变量计数。如此一来,结果是显而易见的,如果其他中断服务程序调用了此函数,且中断优先级高于滴答定时器,会导致滴答定时器中断服务程序一直得不到执行,从而卡死在里面。所以滴答定时器的中断优先级一定要比它们高。

/* 设置中断优先级分组 */ /* 使用滴答定时器做为默认时基,配置为1ms滴答,另外系统上电后默认使用的HIS时钟 */ /* 初始化底层硬件 */

此函数用于初始化HAL库,此函数主要实现如下功能:

  •   设置滴答定时器的每1ms中断一次。
  •   HAL库不像之前的标准库,在系统启动函数SystemInit里面做了RCC初始化,HAL库是没有做的,所以进入到main函数后,系统还在用内部高速时钟HSI,对于H7来说,HSI主频是64MHz。
  1. 必须在main函数里面优先调用此函数。
  2. 用户务必保证每1ms一次滴答中断。
  3. 关于优先级分组的设置可以看第21章节。

此函数的使用比较简单,上电后优先调用即可。

/* 复位底层硬件初始化 */

此函数用于复位HAL库和滴答时钟。

此函数的使用比较简单,需要调用的时候直接调用即可。

此函数用于初始化滴答时钟,此函数相关问题如下:

  •   此函数有个前缀__weak ,表示弱定义,用户可以重定义。
  •   此函数用于初始化滴答时钟1ms中断一次,并且为滴答中断配置一个用户指定的优先级。
  •   调用基于此函数实现的HAL_Delay要特别注意,因为这个函数的时间基准是基于滴答定时器或者其他通用定时器实现,实现方式是滴答定时器或者其他通用定时器里面做了个变量计数。如此一来,结果是显而易见的,如果其他中断服务程序调用了此函数,且中断优先级高于滴答定时器,会导致滴答定时器中断服务程序一直得不到执行,从而卡死在里面。所以滴答定时器的中断优先级一定要比它们高。

此函数由HAL_Init调用,无需用户操作,除非需要重定义。

调用了函数HAL_Init后,Systick相关的函数就可以使用了。这些函数如下:

这些函数就比较简单了,下面把这些函数实现的功能做个简单的说明:

  •   函数HAL_IncTick在滴答定时器中断里面被调用,实现一个简单的计数功能,因为一般滴答定时器中断都是配置的1ms,所以计数全局变量uwTick每毫秒加1。
  •   函数有个前缀__weak ,表示弱定义,用户可以重定义。

这些函数都比较简单,这里就不举例了。需要的时候,直接调用即可。

此函数用于配置STM32H7内部电压基准大小。

16.3.6 H7自带电压基准相关函数

此函数用于配置STM32H7内部电压基准是否在芯片内部与VREF+引脚接通。

此函数用于内部电压基准的校准调节。

  •   引脚PA0,PA1,PC2,PC3用于ADC时,还有一组对应的可选引脚PA0_C,PA1_C,PC2_C和PC3_C。此函数的作用就是切换可选引脚。关于这个问题的详情可看此贴:。

  •   这两个函数用于使能或者禁止Booster。如果使能了booster的话,在供电电压低于2.7V时,可以减少模拟开关总的谐波失真,这样的话,模拟开关的性能跟正常供电电压时的全范围测量一样。

用于配置BOOT = 0或者BOOT = 1时的启动地址,详情可以看第13章的13.4小节。

分别用于使能或者禁止IO补偿,只有在供电电压范围是2.4V到3.6V之间时,使用此功能才有意义。

分别用于优化IO速度或者禁止优化,不过仅在供电电压低于2.5V时可用,高于2.5V是不可以使用的,另外使用这个功能的前提是用户使能了PRODUCT_BELOW_25V(是可选字节配置选项里面的一个bit)。

用于设置GPIO内部构造中NMOS和PMOS的补偿值,两个形参的范围都是0-15。根据用户调用函数HAL_SYSCFG_CompensationCodeSelect选择的寄存器,这里仅有一个形参的设置是有效的。

低功耗和EXTI相关的函数暂时不做讲解,后面章节用到时候再说明。

学习这个文件注意事项:

  •   系统上电复位后,通过内部高速时钟HSI运行(主频64MHz),Flash工作在0等待周期,所有外设除了SRAM、Flash、JTAG 和 PWR,时钟都是关闭的。这里特别注意SRAM,官方手册里面说的太笼统,这个SRAM到底是指的哪个SRAM。关于这个问题,详情看此贴:。
  1.   AHB和APB总线无分频,所有挂载这两类总线上的外设都是以HSI频率运行。
  2.   所有的GPIO都是模拟模式,除了JTAG相关的几个引脚。
  •   系统上电复位后,用户需要完成以下工作:
  1.   选择用于驱动系统时钟的时钟源。

使能了外设时钟后,不能立即操作对应的寄存器,要加延迟。不同外设延迟不同:

  •   如果是AHB的外设,使能了时钟后,需要等待2个AHB时钟周期才可以操作这个外设的寄存器。
  •   如果是APB的外设,使能了时钟后,需要等待2个APB时钟周期才可以操作这个外设的寄存器。

当前HAL库的解决方案是在使能了外设时钟后,再搞一个读操作,算是当做延迟用。

比如下面使能GPIOA的时钟:

  •   关于时钟方面的知识点补充

1、正确理解HCLK1,2,3,4以及PCLK1,2,3,4对应哪些总线的时钟,下面一张图可以说明问题:

2、内部和外部时钟配置:

高速内部RC振荡器,可以直接或者通过PLL倍频后做系统时钟源。缺点是精度差些,即使经过校准。

低功耗内部RC振荡器,作用同HSI,不过主要用于低功耗。

低速内部时钟,主要用于独立看门狗和RTC的时钟源。

高速外部晶振,可接4 - 48MHz的晶振,可以直接或者通过PLL倍频后做系统时钟源,也可以做RTC的是时钟源。

低速外部晶振,主要用于RTC。

时钟安全系统,一旦使能后,如果HSE启动失败(不管是直接作为系统时钟源还是通过PLL输出后做系统时钟源),系统时钟将切换到HSI。如果使能了中断的话,将进入不可屏蔽中断NMI。

主锁相环PLL1,用于给CPU和部分外设提供时钟。

专用锁相环PLL2和PLL3,用于给部分外设提供时钟。

  •   AHB总线时钟是通过系统时钟分频产生的,用于给AXI,AHB1,2,3,4和APB1,2,3,4九个总线上的外设提供时钟。另外用户可以通过函数HAL_RCC_GetSysClockFreq获取系统时钟。
  •   所有外设时钟都可以通过系统时钟获取,并且部分外设还支持多个时钟源,比如下面串口1,6

此函数用于RCC复位函数,主要实现如下功能:

  1. 此函数不会修改外设时钟,LSI、LSE和RTC时钟。

此函数的使用比较简单,需要调用的时候直接调用即可。

通过上面函数原型,我们可以一目了然的看出此函数的作用,配置了HSE、HSI、CSI、LSI、HSI48、LSE和PLL。

  1. 此函数会更新全局变量SystemCoreClock的主频值,并且会再次调用函数HAL_InitTick更新系统滴答时钟,这点要特别注意。
  2. 系统上电复位或者从停机、待机模式唤醒后,使用的是HSI作为系统时钟。以防使用HSE直接或者通过PLL输出后做系统时钟时失败(如果使能了CSS)。
  3. 目标时钟就绪后才可以从当前的时钟源往这个目标时钟源切换,如果目标时钟源没有就绪,就会等待直到时钟源就绪才可以切换。
  4. 根据设备的供电范围,必须正确设置D1CPRE[3:0]位的范围,防止超过允许的最大频率。
选择PLL的输出作为系统时钟

此函数的作用是配置MCO1(PA8引脚)和MCO2(PC9引脚)的时钟输出以及选择的时钟源,通过下面的截图可以很好的说明此函数的作用:

  •   第2个形参用于选择输出的时钟源,MCO1可选择的时钟源如下:

MCO1可选择的时钟源如下:

此函数的使用比较简单,需要在MCO引脚输出时钟的时候,直接调用即可。

剩下的这些函数都比较简单了,我们这里就不做讲解了。

这个库文件主要功能是NVIC,MPU和Systick的配置。此文件有个臃肿的地方,里面的API其实就是将ARM的CMSIS库各种API重新封装了一遍。这么做的好处是保证了HAL的API都是以字母HAL开头。

更多NVIC相关知识需要大家看第21章节。

更多SysTick相关知识需要大家看第22章节。

更多MPU相关知识需要大家看第23章节。

这些函数的操作就比较容易了,这里就不做讲解了,用到时调用即可。

这些函数的操作就比较容易了,这里就不做讲解了,用到时调用即可。

此函数在本教程第23章有专门的讲解。

本章节就为大家讲解这么多,对于一些常用的函数,望大家务必要掌握。

定时器,顾名思义是拿来定时的,但除了定时之外,我们还可以利用定时器的计数特性,演变出其他有意思的功能出来(诸如控制马达转速)。不过在这之前,我们还是先了解了解定时器的工作原理叭。

下面我们先来捋捋定时器的一些重要概念,只有相关概念了解熟悉了,才好继续玩耍不会学懵逼不是嘛。

这里指计数器的位数。如:CM3内核中Systick定时器的计数位有24位,即最大计数值为 224 -1= 16,777,215。我们就说Systick的计数宽度为24。

定时器是人设计,而设计是自由的。既然你规定了计数的数值范围,那么我们从0数到16,777,215,跟从16,777,215 数到 0 都是可以的不是嘛,所以这就引申出了工作模式的概念:

这两种模式,从本质上来说没有任何区别。不过某些定时器可能没有给与我们开发者辣么自由的操作空间,限定了只可以向上计数or只可以向下计数。

既然我们规定了计数的范围,那么从1计数到2,从2计数到3之间的时间周期是多少?我们如何实现计数周期的不变?

这个问题其实很简单。。。利用时钟啊!时钟的脉冲信号变化规律是很容易确定的。那么我们就可以直接使用时钟实现计数周期,即:

啥?刚刚说完计数周期,你现在给我来个计数时长?啥玩意?

哦,这哥们是拿来中断用的。

我们一开始提到了计数宽度,它决定了我们可以取到的最大计数值。但它也只是决定了我们可以取到的最大计数值,实际计数值CNT(count的简写)是多少,这不还是由我们自己定的嘛。根据项目需求,你想取多少就多少。即:

当向上计数or向下计数达到了CNT次后,计数就会溢出触发一次定时器中断。这个中断有什么用?别急,后面我们我讨论到它。

刚刚我们已经说了当向上计数or向下计数达到了CNT次,就会触发一次定时器中断。那么中断结束之后,计数器怎么继续计时?很简单,设计一个专门控制检测计数状态的模块,当每次计数值达到了我们指定的CNT后,就重置计时器,让它重新计数就OK了。

而这个重置操作,一般称为重装载计数器,我们可以通过操作相关寄存器去控制它。

1.6、定时器简易模型

根据以上信息,我们可以构筑出下图的定时器模型:

SysTick—系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。也常称为滴答定时器,因为它只要简单的计数功能。

系统定时器是一个 24bit 的向下递减的计数器(PS:这里只能向下计数),计数器每计数一次的时间为 1/SYSCLK。

因为 SysTick 是属于 CM3 内核的外设,所以所有基于 CM3 内核的单片机都具有这个

系统定时器,使得软件在 CM3 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。

(以上为本人从野火的资料中,截取修改而来。)

对于Systick而言,有4个寄存器控制它,分别是:

有了上一节的内容做铺垫,对于这几个寄存器的出现应该不难理解。而且,如果我们需要直接操作寄存器的话,一般来说只需要操作前3个寄存器即可。当然,我们现在一般都直接用固件库操作啦。

而对于Systick的应用,我们一般都直接用于实现延时函数,做到精确延时。

2.2.1、延时函数实现原理

从前面的介绍我们知道,当计数器的值到了我们指定的CNT后,会产生一次中断。

我们可以定义一个变量Delaytime。每次中断的时候都对Delaytime--处理。在延时函数中,我们只需要传入我们想要延时的时长time给Delaytime,判断Delaytime是否为0即可。

具体操作上,我们只需要调用固件库提供的库函数Systick_Config()即可。

图源野火的stm32文档

这个函数只需要我们传入计数器的值,大大的简化了操作步骤,结合前面的简易模型看看:

那么一般传入参数是多少呢?以stm32f10x系列为例,系统时钟一般为72Mhz,因为单片机执行指令的速度是微秒级的,所以我们一般设定一次中断时间为1ms,即一般传参为72000。

只讲原理不讲应用示例都是耍流氓。。。

这里我使用野火的指南者实现1s的LED闪烁为举例。

2.3.2、中断服务函数

《零死角玩转STM32—F103指南者》

我要回帖

更多关于 kafka副本同步机制 的文章

 

随机推荐