查看原文
其他

关于STM32的计数和延时

strongerHuang strongerHuang 2021-01-31


读者*丽杰*问

问个问题,我想要获得比较准确的延时时间,用stm32哪个时钟,通过什么方法让他准确?


我的回答

通过STM32的任意一个TIM定时器都可以达到比较精确的延时时间。


关于STM32的计数和延时

在STM32中,具有计数(或计时)功能的模块基本都能实现延时功能。如:系统滴答SysTick、定时器TIM、实时时钟RTC、看门狗WDG。


精确延时一般使用定时器TIM即可实现。当然,是否精确,取决于你的主频(也就是晶振)是否准确,如果主频精确,那么实现的延时也一定精确。


一般来说,常温下实现us微秒级的延时,误差还是挺小的(应该说挺精确)。拿F407,主频168M来说,可以实现几十ns纳秒的延时,如果选用高精度的晶振,误差还是很小的。


总结:想要TIM定时器实现高精确的延时,就需要高精度的晶振。主频精确,那么延时就精确。


STM32的TIM定时器

 STM32的定时器有3类

  • 高级定时器Advanced control Timer

  • 通用定时器General purpose Timer

  • 基本定时器Basic Timer


STM32的这三种定时器都能实现最基本的定时计数功能。差异在于它们的功能多少不一样,从结构图一目了然,下面以STM32F4为例,给大家展示一下F4三类定时器的结构图:


高级定时器



通用定时器



基本定时器



相信大家看了上面3种结构图,心里大概应该明白它们的差异存在哪些地方。 具体差异可以参看手册中的“主要特性”,里面详细讲述了其中的功能和特性。


重要提示

1.定时器的位数有16位和32位之分,详见数据手册


2.定时器有多少个,以及是TIM几,同样见数据手册。如下图STM32F411就只有8个定时器,没有基本定时器。


STM32定时器计数延时原理

上面3种定时器(高级、通用和基本)都能实现计数延时的功能,我们以最简单的基本定时器为例,还是参看着结构图来说明:



1.来自RCC的时钟,参看RCC时钟树,一般是SystemCoreClock或者SystemCoreClock/2, 如STM32F429的就是(180M/2)。


2.分频CK_PSC之后就是计数器CK_CNT的计数频率。

如分频值位9,则计数频率为1M. (180M / 2 / 9 = 10M).


3.实现1us计数:

上面1秒计数10M个数,那么我计10个数,就是1us的时间。只需要在自动重载寄存器ARR中填充10 - 1即可。


代码分析:


红定义

//计数时钟(相当于1秒钟计数10M次个脉冲)
#define TIM6_COUNTER_CLOCK        10000000

//预分频值
#define TIM6_PRESCALER_VALUE      (SystemCoreClock/2/TIM6_COUNTER_CLOCK - 1)

//定时周期(计数满10个算一个周期,也就是1us)
#define TIM6_PERIOD_TIMING        (10 - 1)


配置

void TIM6_Configuration(void) {  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

 /* TIM6时基单元配置 */  TIM_TimeBaseStructure.TIM_Prescaler = TIM6_PRESCALER_VALUE;        //预分频值  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;        //向上计数模式  TIM_TimeBaseStructure.TIM_Period = TIM6_PERIOD_TIMING;             //定时周期  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;            //时钟分频因子  TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
//TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);                         //使能"更新"中断
}

如果需要中断,则开启1us中断。



定时器阻塞延时

void TIM6_Nus(uint16_t Times) {  TIM_Cmd(TIM6, ENABLE);                                             //启动定时器  while(Times--)  {    while(TIM_GetFlagStatus(TIM6, TIM_FLAG_Update) == RESET);        //等待计数完成    TIM_ClearFlag(TIM6, TIM_FLAG_Update);                            //清除标志  }  TIM_Cmd(TIM6, DISABLE);                                            //关闭定时器
}



以上代码,之前有分享过类似的,在下载区 STM32F417_第一阶段里面。


提示:

1.这类阻塞延时,仅供学习其原理使用,请结合实际项目修改代码。


2.可以通过定时中断 + 读取计数器的值来获取精确的时间。

比如:1ms中断一次,同时,计数累加。获取计数值 + 累计值也能得出精确延时。 (当然,中断不能太频繁), 此原理,适用于其他SysTick,RTC等具有计数功能的模块。


推荐阅读:

MDK-ARM编译器从V5升级到V6需要做哪些工作?

RTOS多线程(任务)访问同一硬件(如UART)的方法


最后
  1. 置顶公众号,不怕找不到我;

  2. 记得给我点赞哦!

  3. 微信搜索“strongerHuang” 或者扫描下面二维码、关注,在我的底部菜单查看更多精彩内容!

长按识别二维码 关注



    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存