本文是微控制器中“定时器/计数器”功能系列的第一篇。重点不在特定的微控制器系列上,而是一般地描述定时器功能。我们首先看一下大多数计时器类型的共同特性。然后,我们介绍周期性计时器及其用法。我们以SysTick计时器为例进行详细说明。随后的文章介绍其他常见的计时器。
共同特征
让我们从许多定时器的常见功能入手。
![6dcae2c9a503cec34a1b8f61b2e16ad9.png](https://i-blog.csdnimg.cn/blog_migrate/3789fdaafd0c2b49f83559020b4d3c2d.jpeg)
定时器在系统中的位置
每个定时器都需要一个时钟源或时钟基准。通常有几种可能的时钟源,其中一个是通过开关或多路复用器选择的。有时可以选择使用外部时钟源。为了增加计数范围,选定的时钟进入“预分频器”,该时钟在进入主计数器之前先对其进行分频。预分频器的分频因子通常限于2的幂。例如,20到27给出1,2,4,4,8,16,32,64或128的选择。某些预分频器最高可达216或65,536。预分频器的输出进入一个主计数器,该计数器通常为16位宽,计数范围为0至65535。有时可以将两个16位计数器组合成一个32位计数器,但是大多数定时功能都可以通过预分频器和16位主计数器来实现。主计数器的计数范围由存储在寄存器中的“模”值(M)设置。下面是一个典型的计数序列,其中M =999。这是一个递减计数器,在其达到0后的下一个时钟重新加载模数值。
999 (M = 999) 998 997 . . 2 1 0 (Reload M when counter reaches 0) 999 998 . .
注意,由于“ 0”状态,计数状态数是M +1还是1,000。确保检查定时器的模量负载特性。
控制逻辑定义了定时器的类型,并且对于不同的类型有很大的不同。控制逻辑中包括控制和状态寄存器。为了简单起见,这些寄存器未在图中显示,无论如何,它们对于不同的定时器都大不相同。
初始配置通常包括:
选择时钟源
设置预分频器的除法因子
设定模量值
设置各种控制位
如果使用,启用处理器中断
如果触发DMA操作或其他外设,请设置触发条件
如果定时器连接到输入或输出引脚,则设置引脚连接。通常,这是微控制器常规配置的一部分。
轮询和中断
主程序和计时器是异步的,这意味着计时器独立于程序流进行操作。该程序可以轮询计时器以获取计时器信息。轮询是定期读取状态寄存器,以检测计时器事件或计数器的当前值。这种类型的协调会占用大量处理时间,或者响应时间因复杂的程序而变化很大。这些问题的解决方案是使用中断。下图是显示使用定期中断的典型程序流程图。
![9e5b67a496d9f3449f51c1f4a29abc30.png](https://i-blog.csdnimg.cn/blog_migrate/75743f48ab2ad1e136ed92bacf58d2e6.jpeg)
使用定期中断的典型程序流程图
“ Main”是在处理器上运行的常规程序。发生计时器事件并触发中断。例如,当递减计数计时器达到0并在主计数器中重新加载模数时,就会发生中断。计时器将硬件信号发送到“中断控制器”,该控制器中止主程序的执行,并使处理器跳转到称为“中断服务程序”或ISR的软件功能。 ISR有时称为“中断处理程序”或“异常处理程序”。 ISR完成后,将继续执行主程序。注意主程序如何不花时间检查计时器事件。对计时器事件的响应可以非常快速且可预测。
刚开始从事微控制器的设计人员有时会避免中断,因为它们看起来很复杂。但是,计时器和中断可以很好地协同工作。而且,定时器可以是学习中断的好方法,因为所有内容都在微控制器内部并且可以无需外部连接或设备来通过调试器看到。从下面介绍的简单“定期计时器”开始。使它正常运行而不会中断。然后,设置中断控制器并在计时器中启用中断。
ISR可能非常简单。像这样的“伪代码”清单就足以开始使用。本文稍后提供了一个非常短的ISR的真实示例。
/* Simple timer ISR pseudo-code */-Declare a global variable called timerTicks;
/* This is the timer interrupt service routine */
timerISR{
-Clear the interrupt flag in the timer
-Add 1 to timerTicks
-Return
};
周期定时器
周期定时器非常适合学习计时器。它们相对简单,并且在调试器中很难观察。周期定时器会产生固定周期的重复标记或“滴答”,如下所示。主要参数是使用反模数设置的周期。
![aad4c865228874c6000268381a06e649.png](https://i-blog.csdnimg.cn/blog_migrate/2edae987f3f9744017d47cfb43142a74.jpeg)
周期定时器
在定时器的严格控制下,“ A”使动作“ B”周期性地发生。一些例子包括:
- 控制数字输入(如按钮)的轮询
- 通过实时操作系统(RTOS)安排任务
- 精确调整DMA传输到数模转换器的速度
- 触发模数转换器以获得准确的采样率
触发模数转换器是一个有趣的情况。考虑下图中所示的动作链:
![7b8b78590eb8edf5c62405cc7afc3f82.png](https://i-blog.csdnimg.cn/blog_migrate/bbf008361502691ee035fd94244afdc9.jpeg)
触发(triggering)模数转换器(ADC)
周期性定时器通过触发模拟信号的模数转换来开始工作。转换完成后,模数转换器告诉DMA(直接内存访问)控制器将结果移至内存。然后,一切都等待来自周期性计时器的另一个触发。当获取了所需的数据量时,DMA控制器通知主程序。该数据采集的一个重要部分是通过周期性计时器进行精确的步调,而不会因软件而造成时序的不确定性。此外,该软件完全可以自由执行其他操作。使用某些低功耗微控制器,处理器,ADC和DMA甚至可以在等待过程中进入睡眠状态并节省功耗。
延迟
延迟功能会在一段时间后导致某些事情发生。该图显示了“ A”将计时器重置为0并在“ B”发生之前启动了三个滴答的延迟。
![38d7a359b041e5699b0bd0439d0214b6.png](https://i-blog.csdnimg.cn/blog_migrate/3ecb97cc273d7f50d8df08748509e190.jpeg)
定时器中的延迟功能
延迟的一个例子是取消按钮弹跳。通过轮询输入来检测按钮的初次按下,或者存在中断。然后,通过“ A”开始延迟,以等待按钮的任何机械弹跳结束。延迟之后,按钮将再次在“ B”中采样。如果按钮仍处于活动状态,则检测到有效的按钮按下。使用延迟计时器可以使处理器在去抖动时间内执行其他操作,并且延迟是准确且可重复的。
简单的周期定时器的详细说明
我们想更详细地说明一个定期计时器。我们知道的最简单的是“ SysTick计时器”,或者简称为SysTick。 SysTick定时器是一些ARM处理器随附的选件,这些处理器被用作许多32位微控制器的核心。这是SysTick定时器的示意图。
![5237df410bd6b70bfe5f138998a2166a.png](https://i-blog.csdnimg.cn/blog_migrate/5a7c0474cc186b5f0592282fa937004a.jpeg)
SysTick定时器
同样,为简单起见,我没有显示很多细节。 时钟选择是处理器时钟或微控制器设计人员选择的可用时钟。 在我当前使用的微控制器中,该时钟为处理器时钟的16分频。计数器为24位宽,并从模数值开始递减,该模数值在计数器达到0时重新加载。计数序列与前面显示的相同。 可以从“当前值”寄存器中“即时”读取计数器的值。 当计数器达到0时,定时器可以请求中断。 这与定期计时器一样简单。
这是一个简短的例程,用于使用48 MHz处理器时钟在1毫秒的时间内初始化SysTick计时器。 只需要写入三个SysTick寄存器。
void init_sysTick(void){ SYST_RVR = 47999; // Load count modulus or “reload value”. SYST_CVR = 0; // Write 0 to the “current value” register // to initialize the counter SYST_CSR = 0x7; // Write to the control and status register // to select the processor clock, // enable interrupts and start counting}
为什么模数为47,999,而不是48,000? 记住计数器的额外计数为0。
SysTick和其他定期计时器通常通过与软件合作来扩展。 长期准确计时的常用方法是使用软件变量,该变量随来自计时器的每次中断而增加。 此变量通常称为“滴答计数器”。 这是一个中断服务程序或“异常处理程序”的示例,该示例增加了一个称为“ sysTicks”的变量,以保持已过去的SysTick定时器周期数的运行计数。
void SysTick_Handler(void){ extern uint32_t sysTicks; sysTicks++;}
请注意,SysTick计时器甚至不需要清除中断标志!如果sysTicks(变量,而不是计时器)为32位,则会将最长期限延长到47年。实际上,SysTick计时器通常设置为方便的时间单位,例如1或10毫秒。然后,使用该时间段作为时基,使用软件构建其他计时。一个常见的示例是将SysTick用作实时操作系统的心跳,用于切换任务和提供其他服务。
提示
高速计时器(例如以48 MHz运行的SysTick)是一种高分辨率测量关键代码执行时间的好方法。对于48 MHz,定时分辨率为±21纳秒。在关键部分的开头初始化并启动计时器。在本节末尾,请阅读SysTick计数器。
要获得代码部分的执行时间,请从模数(起始计数)中减去计数器值,然后将此差值乘以处理器时钟周期。有趣的是,您看到对代码所做的更改或在编译器中启用不同级别的优化的效果。
下一步是什么
周期计时器对于安排事件起搏,创建延迟以及充当实时操作系统或其他调度功能的心跳非常有用。该系列的下一篇文章介绍了一种不同类型的定时器,该定时器使用脉冲宽度调制为外部世界提供了灵活的接口。