7. 定时器
7.1 定时器介绍
定时器是单片机内部集成,可以通过编程控制。单片机的定时功能是通过计数来实现的,当单片机每一个机器周期产生一个脉冲时,计数器就加一。定时器的主要功能是用来计时,时间到达之后可以产生中断,提醒计时时间到,然后可以在中断函数中去执行功能。比如我们想让一个 led 灯 1 秒钟翻转一次,就可以使用定时器配置为 1 秒钟触发中断,然后在中断函数中执行 led 翻转的程序。
7.2 硬件定时器和软件定时器
定时器可以基于硬件也可以基于软件实现,两者有各自的特点和适用场景:
- 硬件定时器是由微控制器硬件提供的定时功能,由专门的计时/计数器电路实现。硬件定时器的最大优势在于精确度高和可靠性强,因为它们不受软件任务和操作系统调度的影响。当需要非常精确的定时功能,如产生PWM信号或者获取精确的时间测量时,硬件定时器是首选。由于定时操作由硬件直接完成,即使主CPU忙于其他任务,定时器仍然可以在预定时间到达时准确地执行回调操作。
- 软件定时器是由操作系统或者软件库实现的定时器,它们利用操作系统提供的机制来模拟定时器功能。软件定时器的实现受到当前系统负载和任务调度策略的影响,因此相对来说不如硬件定时器精确。但是软件定时器通常更灵活,可以创建大量的定时器,适用于不需要精确时间控制的场合。
在某些情况下,软件定时器可能会引起定时精度问题,例如在高负载条件下,或者当系统中有许多其他高优先级任务时。对于不需要高精度的简单延时,软件定时器通常足够使用。
7.3 定时器的限制和性能考虑
在MicroPython环境中,虽然定时器的使用带来了方便,但也存在一些限制和性能方面的考量:
- 资源限制:在MicroPython中,可用的硬件定时器数量有限,因此必须谨慎选择何时何地使用硬件定时器。
- 回调函数限制:定时器的回调函数是在中断上下文中调用的,这意味着回调函数中的代码必须是简短且执行速度快的。在这些回调函数中执行复杂的操作或长时间的任务可能会导致系统性能下降,影响其他定时任务的准确性。
- 中断屏蔽问题:如果回调函数中有过多的计算,或者与其他中断共享资源,可能会出现中断屏蔽的情况,从而阻塞其他重要中断的处理。
- 线程和并发:MicroPython通常运行在没有操作系统的环境中,而且在标准实现中不支持多线程。这表明所有操作都是顺序执行的,某些长时间执行的任务可能会阻塞其他代码的执行,包括定时器的响应。
- 垃圾回收影响:在MicroPython中,内存管理是通过垃圾回收实现的,垃圾回收的执行是不可预知的且可能占用可观的时间,这在一定程度上影响定时任务的执行。
在设计使用定时器的系统时,建议编写尽可能简短和高效的回调函数。涉及重量级操作或者可能阻塞的代码应当提供给独立的任务或通过其他机制异步处理,以避免影响定时器的准时响应。
7.4 定时器基本参数
ESP32S3芯片提供了集成的硬件定时器,具有多个硬件定时器,例如 Timer0、Timer1、Timer2 等。这些定时器能用于精确的时间控制任务,比如周期性操作、PWM信号生成和事件计数。它们是由微控制器的硬件直接支持的,可以配置为单次或者周期触发模式,并且能在设定时间到达时生成中断,从而执行快速的任务或处理。在使用MicroPython开发时,有专门的类可以让我们易于访问和使用这些硬件资源。基本的定时器参数包括:定时器号、通道号、预分频器、自动重新加载值、定时器中断使能等。
资料参考官方MicroPython文档: http://www.86x.org/en/latet/library/machine.Timer.html#machine-timer
7.5 使用定时器流程
1.首先需要导入必要的模块和库
from machine import Timer
- 接下来可以创建并初始化定时器对象
timer = Timer(-1) # 使用一个空闲的定时器
其中,-1表示的是使用空闲的未指定的定时器。-1参数也可以修改为0,1,2,3。 表示的是使用定时器0,定时器1等等。
3.创建一个定时器会自动调用的处理函数
例如,以下是一个简单的用于打印 “Tick” 的函数,每次计时器触发时会调用它
def tick(timer):
print('Tick')
```
4. 初始化定时器
使用 init() 方法来初始化定时器,并将其设置为周期性模式,同时指定中断处理函数。例如,以下的代码会每 1 秒钟调用一次 tick 函数;
# 1000 毫秒,也就是 1 秒
timer.init(period=1000, mode=Timer.PERIODIC, callback=tick)
其中的参数说明如下:
1. period:指定定时器溢出之间的时间,以毫秒为单位。例如,period=1000 设置定时器的周期为1秒。
2. mode:设置定时器的工作模式。常见的模式有:
- Timer.ONE_SHOT:定时器只会运行一次,到达设定的时间后触发一次回调函数,然后停止。
- Timer.PERIODIC:定时器会不断重置,每次达到设定的周期都会触发回调函数。
3. callback:这是一个函数指针,会在定时器溢出时被调用。必须是一个接受定时器对象作为唯一参数的函数。
2
3
4
5
6
7
8
9
10
11
12
13
- 最后,如果想停止定时器或释放定时器资源,则可以使用 deinit() 方法
timer.deinit()
这将停用定时器,可以在需要的时候重新初始化它。
下面是一个完整的示例:
from machine import Timer
def tick(timer):
print('Tick')
timer = Timer(-1)
timer.init(period=1000, mode=Timer.PERIODIC, callback=tick)
# ... 这里可以是你的其它代码,1 秒钟一次,'Tick' 将会被打印出来。
# 当你想停止定时器时:
# timer.deinit()
2
3
4
5
6
7
8
9
10
11
7.6 硬件连接与准备
本案例通过LED测试定时器的定时效果,将LED状态变化放在定时器中断处理函数中,设置定时器的定时时间为1秒,当定时器的定时时间到时,就会触发中断,将LED的状态改变;
7.7 定时器验证
import time
from machine import Pin, Timer
# 定义 Pin 控制引脚
led = Pin(48, Pin.OUT)
# 定义定时器中断的回调函数
def timer_irq(timer_pin):
led.value(not led.value())
# 定义定时器
timer = Timer(0)
# 初始化定时器
timer.init(period=500, mode=Timer.PERIODIC, callback=timer_irq)
while True:
time.sleep(1)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
7.8 定时器效果
每隔0.5秒亮灭一次。