【原创】技术系列之 定时器(二)
单片机原理及接口技术(C51编程)第7章 定时器计数器
图7-14 由外部计数输入信号控制LED的闪烁
(3)设置IE寄存器 本例由于采用T1中断,因此需将IE寄存器的EA、ET1位置1。
(4)启动和停止定时器T1 将寄存器TCON中TR1=1,则启动T1计数;TR1=0,则停止T1计数。
参考程序如下:
#include <reg51.h> void Delay(unsigned int i)
7.4 定时器/计数器的编程和应用 4种工作方式中,方式0与方式1基本相同,只是计数位数不同。方
式0为13位,方式1为16位。由于方式0是为兼容MCS-48而设,计数初 值计算复杂,所以在实际应用中,一般不用方式0,常采用方式1。
7.4.1 P1口控制8只LED每0.5s闪亮一次 【例7-1】在AT89S51的P1口上接有8只LED,原理电路见图7-
当TMOD的低2位为11时,T0被选为方式3,各引脚与T0的逻辑关系 见图7-8。
T0分为两个独立的8位计数器TL0和TH0,TL0使用T0的状态控制位 C/T* 、GATE、TR0 ,而TH0被固定为一个8位定时器(不能作为外部 计数模式),并使用定时器T1的状态控制位TR1,同时占用定时器T1的 中断请求源TF1。
13。采用T0方式1的定时中断方式,使P1口外接的8只LED每0.5s闪亮 一次。
23
图7-13 方式1定时中断控制LED闪亮
24
(1)设置TMOD寄存器 T0工作在方式1,应使TMOD寄存器的M1、M0=01;应设置C/T*=0,为定
时器模式;对T0的运行控制仅由TR0来控制,应使相应的GATE位为0。定时 器T1不使用,各相关位均设为0。所以,TMOD寄存器应初始化为0x01。 (2)计算定时器T0的计数初值
STC89C52单片机定时器2详细整理
STC89C52单片机定时器2详细整理51单片机是我自学的第一款单片机,那时正好是过春节,想起那个寒假,外面下着大雪,有时还会传来一两声爆竹的炸响,而我一个人在房间里摆弄单片机开发板,反复调试程序的时光,真是难忘!我自认为这款单片机所有的资源中最不好搞清楚的就是定时器2,尤其是对于那些以前从来没有玩过单片机的新手。
定时器2是新增资源,也是51单片机定时器里面功能最强大的一个定时器。
所以掌握好定时器2还是非常有必要的。
以下是在我完全搞明白它的原理和用法的基础上整理的一篇小文章。
读起来,好像Datasheet一样!请原谅,希望没有辜负你的点击!定时器2是一个16位定时器/计数器,通过设置特殊功能寄存器T2CON中的C/T2位可将其设置为定时器或是计数器;通过设置T2CON中的工作模式选择位可将定时器2设置为三种工作模式,分别为捕获、自动重新装载(递增或是递减计数)和波特率发生器。
知识点一、定时器2的控制寄存器T2CON(可按位寻址)*D7位--TF2:定时器2溢出标志位。
用于请求中断(必须由软件清0)D6位--EXF2:定时器外部标志位。
当外部信号使能时,发生外部负跳变时置位请求中断(必须由软件清0)D5位--RCLK:接受时钟标志位。
默认情况下串行口中模式1和模式3的时钟是由定时器1的溢出率提供,若该位置位,则由定时器2提供。
D4位--TCLK:发送时钟标志位。
原理同上D3位--EXEN2:定时器2的外部使能标志位。
定时器2没有作为串行口时钟时,若将该位置位时,将允许T2EX的负跳变产生捕获或重装D2位--TR2:定时器2启动/停止控制位。
D1位--C/T2:定时器2的定时器/计数器选择位(在reg52头文件中定义为了C_T2,请注意,下面相同)D0位--CP/RL2:捕获/重装标志位。
知识点二定时器2的模式控制寄存器T2MOD(不可按位寻址)该寄存器在单片机的头文件reg2.h中可能没有被定义,自己定义吧!D1位—T2OE:定时器2输出使能位D0位—DCEN:向下计数使能位知识点三:定时器2的三种模式**1、捕获模式*在'CP/RL2=1'&&'TR2=1'时进入捕获模式。
STC89C52单片机定时器2的使用
STC89C52单片机定时器2的使用实现定时和计数的方法一般有:软件定时、专用电路和可编程定时器/计数器三种方法。
软件定时:执行一个循环程序进行时间延迟。
定时准确,不需要外加硬件电路,但会增加CPU 开销。
专用硬件电路定时:可以实现请精确的定时和计数,但参数调节不方波。
可编程定时器/计数器:不占用CPU 时间,能与CPU 并行工作,实现精确的定时和计数,又可以通过变成设置其工作方式和其他参数,使用方便。
以下说明仅试用宏晶的STC89C52!!定时器2:T2MOD,T2CON,TH2,TL2,RC2H,RC2L.T2MOD:0C9H(不可位寻址)000000T2OEDCENT2OE:定时器输出使能位DECN:向上/向下计数使能位。
定时器2 可配制成向上/向下计数器。
0:向上计数(模式状态) 1:向下计数(尽量不使用)T2CON:0XC8H(可位寻址)TF2EXF2RCLKTCLKEXEN2TR2C/T2CP/RL2TF2:7 上/下溢出标志位,定时器2 溢出时置位,必须有用软件清零!当RCLK 或TCLK=1 时,TF2 将不会置位。
EXF2:6 定时器2 外部标志,当EXEN2=1 且T2EX 的负跳变产生捕获或重装时,EXF2 置位。
定时器2 中断使能时,EXF2=1 将使CPU 从中断向量处执行定时器2 中断子程序。
EXF2 位必须用软件清零。
在递增/递减计数器模式(DCEN=1)中,EXF2 位不会引起中断。
RCLK:5 接收时钟标志。
RCLK 置位时,定时器2 的溢出脉冲作为串口模式1 和模式3 的接收时钟。
RCLK=0 时,将定时器1 的溢出脉冲作为串口模式1 和模式3 的接收时钟。
TCLK:4 发送时钟标志位。
TCLK 置位时,定时器2 的溢出脉冲作为串口模式1 和模式3 的发送时钟。
TCLK=0 时,将定时器1 的溢出脉冲作为串口模式1 和模式3 发送时钟。
EXEN2:3 定时器2 外部使能标志。
89C52定时器2
定时器T2的功能比T1、T0都强大,但描述它的资料不多,可能是使用得比较少的缘故吧。
它是一个16位的具有自动重装和捕获能力的定时/计数器,它的计数时钟源可以是内部的机器周期,也可以是P1.0输入的外部时钟脉冲。
T2的控制寄存器的功能描述如下:T2CON(T2的控制寄存器),字节地址0C8H:位地址 0CFH 0CEH 0CDH 0CCH 0CBH 0CAH 0C9H 0C8H符号 TF2 EXF2 RCLK TCLK EXEN2 TR2 C/T2 CP/RT2各位的定义如下:TF2:定时/计数器2溢出标志,T2溢出时置位,并申请中断。
只能用软件清除,但T2作为波特率发生器使用的时候,(即RCLK=1或TCLK=1),T2溢出时不对TF2置位。
EXF2:当EXEN2=1时,且T2EX引脚(P1.0)出现负跳变而造成T2的捕获或重装的时候,EXF2置位并申请中断。
EXF2也是只能通过软件来清除的。
RCLK:串行接收时钟标志,只能通过软件的置位或清除;用来选择T1(RCLK=0)还是T2(RCLK=1)来作为串行接收的波特率产生器TCLK:串行发送时钟标志,只能通过软件的置位或清除;用来选择T1(TCLK=0)还是T2(TCLK=1)来作为串行发送的波特率产生器EXEN2:T2的外部允许标志,只能通过软件的置位或清除;EXEN2=0:禁止外部时钟触发T2;EXEN2=1:当T2未用作串行波特率发生器时,允许外部时钟触发T2,当T2EX引脚输入一个负跳变的时候,将引起T2的捕获或重装,并置位EXF2,申请中断。
TR2:T2的启动控制标志;TR2=0:停止T2;TR2=1:启动T2C/T2:T2的定时方式或计数方式选择位。
只能通过软件的置位或清除;C/T2=0:选择T2为定时器方式;C/T2=1:选择T2为计数器方式,下降沿触发。
CP/RT2:捕获/重装载标志,只能通过软件的置位或清除。
CP/RT2=0时,选择重装载方式,这时若T2溢出(EXEN2=0时)或者T2EX引脚(P1.0)出现负跳变(EXEN2=1时),将会引起T2重装载;CP/RT2=1时,选择捕获方式,这时若T2EX引脚(P1.0)出现负跳变(EXEN2=1时),将会引起T2捕获操作。
定时器的定义与使用方法
定时器的定义与使用方法1.引言1.1 概述定时器是一种用于计时和调度任务的工具。
它允许我们在特定的时间间隔内执行某个任务,或者在特定的时间点执行某个操作。
定时器在计算机系统中的应用非常广泛,它可以用于控制程序的执行顺序,实现定时任务,以及进行事件触发等。
在计算机领域中,定时器被广泛用于各种应用场景,例如操作系统的任务调度、网络传输的控制、实时系统的处理等。
它可以帮助我们准确地控制时间,实现精确的任务执行。
定时器通常由硬件和软件两部分组成。
硬件定时器通过计时器芯片或者计数器来实现时间的度量和计算,而软件定时器则是通过编程语言提供的函数或者类库来设置和处理定时任务。
定时器的使用方法也非常简单,我们可以通过编程语言中提供的接口来创建一个定时器对象,并设置好时间间隔或者触发时间。
一旦定时器被启动,它将按照预定的时间间隔或者触发时间来执行指定的任务或操作。
总的来说,定时器是一种非常有用的工具,它可以帮助我们实现各种时间相关的任务和操作。
在本文的后续部分中,我们将详细介绍定时器的定义和使用方法,以及一些常见的注意事项和实例应用。
1.2文章结构1.2 文章结构本文主要讨论定时器的定义与使用方法。
为了更好地组织内容并便于读者理解,文章将按照以下结构进行展开:1. 引言:引言部分将概述本文的背景和目的,为读者提供初步认识定时器的必要背景知识。
2. 正文:2.1 定时器的定义:本节将介绍定时器的基本概念和定义。
首先,我们将解释什么是定时器以及其作用。
随后,将从软件和硬件两个角度来讨论定时器的不同类型和工作原理。
2.2 定时器的使用方法:本节将详细介绍定时器的使用方法。
我们将从编程角度出发,讲解定时器在不同编程语言(如C、C++、Python 等)中的使用方法和常见的应用场景。
此外,还将重点介绍定时器的参数设置、中断处理以及注意事项等方面的内容,以便读者能够深入理解和合理使用定时器。
3. 结论:3.1 总结:本节将对全文进行总结,回顾定时器的定义和使用方法。
《定时器及应用举例》课件
。
设置触发条件
根据应用需求设置定时器的触 发条件,如时间到达、外部信 号触发等。
设置时间间隔
根据应用需求设置定时器的时 间间隔,如每隔一定时间触发 一次。
保存设置
完成设置后保存相关参数,确 保定时器能够按照预设参数进
行工作。
04
定时器的应用举例
软件编程
01
02
03
04
选择编程语言
根据定时器的厂商提供的编程 语言进行编程。
编写程序
根据应用需求编写程序,设置 定时器的触发条件、时间间隔
等参数。
调试程序
通过模拟或实际测试,对程序 进行调试,确保定时器能够按
照预期工作。
下载程序
将编写好的程序下载到定时器 中进行测试或实际应用
用于控制室内温度,实现 自动开关机,节省能源。
冰箱
用于控制冷藏和冷冻室的 温度,保持食物的新鲜度 。
洗衣机
用于控制洗涤和漂洗的时 间,实现自动化洗衣。
工业控制领域应用举例
自动化生产线
仪器仪表
用于控制生产线的启动和停止,保证 生产过程的稳定性和效率。
用于控制和监测各种工业设备的运行 状态和参数。
不要将电源直接连接到定时器的输出端,以防设 备损坏和火灾风险。
使用注意事项
设置时间
在设置定时器时间时,确保时间设置正确,避免误操作导致设备 无法正常工作。
安装位置
确保定时器安装在通风良好、干燥、无尘的地方,以防设备过热或 受潮。
定期校准
定期检查和校准定时器,以确保其准确性和可靠性。
维护与保养
清洁外壳
03
定时器的使用方法
Windows中7种定时器
众所周知,Windows 是基于消息机制的系统,任何事件的执行都是通过发送和接收消息来完成的。
这样就带来了一些问题,如一旦计算机的CPU被某个进程占用,或系统资源紧张时,发送到消息队列中的消息就暂时被挂起,得不到实时处理。
因此,不能简单地通过Windows消息引发一个对定时要求严格的事件。
另外,由于在Windows中已经封装了计算机底层硬件的访问,所以,要想通过直接利用访问硬件来完成精确定时,也比较困难。
所以在实际应用时,应针对具体定时精度的要求,采取相适应的定时方法。
VC中提供了很多关于时间操作的函数,利用它们控制程序能够精确地完成定时和计时操作。
本文详细介绍了VC中基于Windows的精确定时的七种方式,如下图所示:图一图像描述方式一:VC中的WM_TIMER消息映射能进行简单的时间控制。
首先调用函数SetTimer()设置定时间隔,如SetTimer(0,200,NULL)即为设置200ms 的时间间隔。
然后在应用程序中增加定时响应函数OnTimer(),并在该函数中添加响应的处理语句,用来完成到达定时时间的操作。
这种定时方法非常简单,可以实现一定的定时功能,但其定时功能如同Sleep()函数的延时功能一样,精度非常低,最小计时精度仅为30ms,CPU占用低,且定时器消息在多任务操作系统中的优先级很低,不能得到及时响应,往往不能满足实时控制环境下的应用。
只可以用来实现诸如位图的动态显示等对定时精度要求不高的情况。
如示例工程中的Timer1。
方式二:VC中使用sleep()函数实现延时,它的单位是ms,如延时2秒,用sleep(2000)。
精度非常低,最小计时精度仅为30ms,用sleep函数的不利处在于延时期间不能处理其他的消息,如果时间太长,就好象死机一样,CPU占用率非常高,只能用于要求不高的延时程序中。
如示例工程中的Timer2。
方式三:利用COleDateTime类和COleDateTimeSpan类结合WINDOWS的消息处理过程来实现秒级延时。
定时器的四种工作模式
节能管理
通过定时器对设备进行定时开关控制, 可以有效节约能源,提高设备的使用 寿命。
定时器的基本概念
定时时间
定时器设定的时间值,到达该时 间值后会触发相应的事件。
定时器精度
定时器的精度决定了其计时的准 确性,高精度的定时器可以提供 更准确的计时服务。
计数方式
定时器可以采用向上计数或向下 计数的方式,不同的计数方式适 用于不同的应用场景。
中断处理
当定时器到达设定时间时,会触 发中断事件,此时可以执行相应 的中断处理函数。
Part
02
定时器的四种工作模式
模式一:单次触发模式
工作原理
在单次触发模式下,定时器只会 在接收到启动信号后开始计时, 并在达到预设时间后输出信号。
应用场景
适用于需要单次计时或延迟控制 的场景,如单次延时启动、单次 脉冲发生等。
应用场景
适用于需要与其他信号同步或受外部条件控制的场景,如电机控制、事件计数 等。
模式四:外部触发模式
工作原理
在外部触发模式下,定时器的计时开始和结束受外部触发信号控制。当接收到外 部触发信号时,定时器开始计时;当再次接收到外部触发信号时,定时器停止计 时。
应用场景
适用于需要与其他设备或系统协同工作的场景,如远程控制、自动化生产线等。
模式二:连续触发模式
工作原理
在连续触发模式下,定时器会不断循 环计时,每次达到预设时间后都会输 出信号,直到接收到停止信号。
应用场景
适用于需要连续计时或循环控制的场 景,如周期性信号发生、PWM波形生 成等。
模式三:门控触发模式
工作原理
在门控触发模式下,定时器的计时开始和结束受门控信号控制。当门控信号为 高电平时,定时器开始计时;当门控信号为低电平时,定时器停止计时。
定时器
• 单片机复位时,两个寄存器的所有位都被清0。
25
方式2的应用实例
方式2省去程序中重装初值的指令,并可产生相当精确的定时。
例:当T0(P3.4)引脚上发生负跳变时,从P1.0引脚 上输出一个周期为1ms的方波,如图所示。(系统时 钟为6MHz)
方式2的应用实例
(1)工作方式选择
T0为方式1计数,初值 0FFFFH,即外部计数 输入端T0(P3.4)发生一次负跳变时,T0 加1且溢出,溢出标志TF0置“1”,发中断 请求。在进入T0中断程序后,把F0标志置 “1”,说明T0脚已接收了负跳变信号。
设定时器工作在方式1,则M=16 X=2M-T/t =216-20 ×103=45536=B1E0H
则:TH0=0B1H,TL0=0E0H
ORG 0000H AJMP MAIN ORG 000BH AJMP T0INT
MAIN: MOV SP,#60H MOV TMOD,#01H MOV TH0,#0B1H MOV TL0,#0E0H MOV IE,#10000010B SETB TR0
•
以上例题采用查询的方法,这种方法很简单,
但是在定时器整个计数的过程中,CPU要不断
地查询溢出标志TFx的状态,很难执行其他操
作,占用了CPU的工作时间,使得CPU的工作
效率不高,在复杂系统中不可取。
•
采用中断的方式来实现,可大大提高CPU的工
作效率,学习重点。
39
运行中读定时器/计数器
在读取运行中的定时器/计数器时,需注意: 若恰好出 现TLX溢出向THX进位的情况,则读得的(TLX) 值就 完全不对。同样,先读(THX) 再读(TLX) 也可能出错。
T1定义为方式2定时。在T0脚发生一次负跳变 后,启动T1每500s产生一次中断,在中断 服务程序中对P1.0求反,使P1.0产生周期 1ms的方波。
定时器的工作原理
定时器的工作原理
定时器是一种常见的设备,它常常被用来计时或者作为一种实现周期性任务的手段。
在工业自动化、智能家居等领域都有着广泛的应用。
那么,定时器是如何工作的呢?
1. 定时器的概念
定时器是一种计时器,可以按照预设的时间间隔进行周期性的计时和触发某些事件。
在数字电路中,定时器通常由计数器和振荡器组成,计数器用于计数,而振荡器则提供时钟信号。
定时器的工作原理可以分为两个部分:计数器和振荡器。
(1)计数器部分
计数器一般采用二进制计数器,它可以根据振荡器提供的时钟信号进行计数。
当计数器的计数值达到设定的阈值后,就会触发定时器的定时事件。
阈值的设定可以通过调节计数器的初始值或者通过预设一个比较器实现。
(2)振荡器部分
振荡器通常由一个晶体振荡器或者RC振荡器构成。
它可以提供一个固定频率的时钟信号,使计数器按照设定的时间间隔进行计数。
振荡器的频率可以通过调整晶体振荡器或者改变RC电路的参数来调节。
3. 定时器的应用
定时器广泛应用于各种电子设备中,如计算机、手机、家电等。
在计算机中,定时器可以用来实现操作系统的调度机制,以及计算机的定时关机等功能。
在手机中,定时器可以用来作为闹钟、定时拍照等功能的实现。
在家电中,定时器常常用来控制灯光、空调、电视等设备的开关。
定时器是一种非常实用的设备,它的工作原理简单易懂,应用领域广泛。
随着技术的不断进步,定时器的功能也越来越强大,对于人们的生活和工作都有着重要的作用。
单片机原理与接口技术第三版课后答案
单片机原理与接口技术第三版课后答案单片机原理与接口技术第三版课后答案【篇一:单片机原理及接口技术课后答案李朝青(第二版)】lass=txt> 第一章1.单片机具有哪些特点(1)片内存储容量越来越大。
(2抗干扰性好,可靠性高。
(3)芯片引线齐全,容易扩展。
(4)运行速度高,控制功能强。
(5 )单片机内部的数据信息保存时间很长,有的芯片可以达到年以100 上。
2.89C51单片机内包含哪些主要逻辑功能部件?答:80C51系列单片机在片内集成了以下主要逻辑功能部件:(l)cpu(中央处理器):8位⑵片内ram:128b(3)特殊功能寄存器:21个⑷程序存储器:4kb⑸并行i/o 口 :8位,4个⑹串行接口 :全双工,1个⑺定时器/计数器:16位,2个(8)片内时钟电路:1个3•什么是微处理器(cpu)、微机和单片机?答:微处理器本身不是计算机,但它是小型计算机或微机的控制和处理部分。
微机则是具有完整运算及控制功能的计算机,除了微处理器外还包括存储器、接口适配器以及输入输出设备等。
单片机是将微处理器、一定容量的ram、rom以及i/o 口、定时器电路集成在一块芯片上,构成的单片微型计算机。
4•微型计算机怎样执行一个程序?答:通过cpu指令,提到内存当中,再逐一执行。
5•什么是嵌入式系统?他有哪些类型?为什么说单片机是典型的嵌入式系统?答;嵌入式系统是将先进的计算机技术、半导体技术和电子技术和各个行业的具体应用相结合后的产物,这一点就决定了它必然是一个技术密集、资金密集、高度分散、不断创新的知识集成系统它有嵌入式微处理器、嵌入式微控制器、嵌入式dsp处理器、嵌入式片上系统等。
嵌入式系统的出现最初是基于单片机的。
它从体系结构到指令系统都是按照嵌入式应用特点专门设计的,能最好的满足面对控制对象,应运系统的嵌入、现场的可靠运行以及非凡的控制品质要求。
因此,她是典型的嵌入式系统。
第二章1.89C51单片机内包含哪些主要逻辑功能部件?答:80C51系列单片机在片内集成了以下主要逻辑功能部件:(l)cpu(中央处理器):8位⑵片内ram:128b(3)特殊功能寄存器:21个(4)程序存储器:4kb⑸并行i/o 口 :8位,4个⑹串行接口 :全双工,1个⑺定时器/计数器:16位,2个(8)片内时钟电路:1个2.89C51的ea端有何用途?3.89c51的存储器分哪几个空间?如何区别不同空间的寻址?答:rom (片内rom和片外rom统一编址)(使用 move )(数据传送指令)(16bits地址)(64kb)片外 ram ( movx)( 16bits 地址)(64kb)片内 ram ( mov)( 8bits 地址)(256b)4.简述89c51片内ram的空间分配。
单片机原理及接口技术(李朝青)课后习题答案——第六章
答:用 T1 控制位 C/T 切换定时器或计数器工作方式就可以使 T1 运行。定时器 T1 无工作模
式 3,将 T1 设置为工作模式 3,就会使 T1 立即停止计数,关闭。
8、以定时器/计数器 1 进行外部时间计数,每计数 1000 个脉冲后,定时器/计数器 1 转为定
时工作方式,定时 10ms 后又转为计数方式,如此循环不止。假定 为 6WHZ,用模式 1 编程。
16、89C51 单片机的定时器在何种设置下可提供三个 8 位计数器定时器?这时,定时器 1 可
作为串行口波特率发生器。若波特率按 9600b/s,4800b/s,2400b/s,1200b/s,600b/s,100b/s 来考虑,
则此时可选用的波特率是多少(允许存在一定误差)?设 fosc=12MHz。
SJMP LOOP2
;时间未到,转 LOOP2,继续查询
9、一个定时器定时时间有限,如何实现两个定时器的串行定时以满足较长定时时间的要求?
答:当一个定时器溢出时,设置另一个定时器的初值为 0 开始定时。
10、使用一个定时器,如何通过软硬件结合方法实现较长时间的定时?
答:设定好定时器的定时时间,采用中断方式用软件设置计数次数,进行溢出次数累计,从
置 TMOD 中的 M1M0 为 00
(2) 模式 1:与模式 0 的唯一差别是寄存器 TH 和 TL 以全部 16 位参与操作。定时时间
t=(216-初值)×振荡周期×12;计数长度位 216=65536 个外部脉冲
置 TMOD 中的 M1M0 为 01
(3) 模式 2:把 TL0 和 TL1 配置成一个自动重装载的 8 位定时器/计数器。TL 用作 8 位
18、 设 fosc=12MHz。试编制一段程序,功能为:对定时器 T0 初始化,使之工作在模式 2,
05_STM32F4通用定时器详细讲解
STM32F4系列共有14个定时器,功能很强大。
14个定时器分别为:2个高级定时器:Timer1和Timer810个通用定时器:Timer2~timer5 和 timer9~timer142个基本定时器: timer6和timer7本篇欲以通用定时器timer3为例,详细介绍定时器的各个方面,并对其PWM 功能做彻底的探讨。
Timer3是一个16位的定时器,有四个独立通道,分别对应着PA6 PA7 PB0 PB1 主要功能是:1输入捕获——测量脉冲长度。
2 输出波形——PWM 输出和单脉冲输出。
Timer3有4个时钟源:1:内部时钟(CK_INT ),来自RCC 的TIMxCLK2:外部时钟模式1:外部输入TI1FP1与TI2FP23:外部时钟模式2:外部触发输入TIMx_ETR ,仅适用于TIM2、TIM3、TIM4,TIM3,对应着PD2引脚4:内部触发输入:一个定时器触发另一个定时器。
时钟源可以通过TIMx_SMCR 相关位进行设置。
这里我们使用内部时钟。
定时器挂在高速外设时钟APB1或低速外设时钟APB2上,时钟不超过内部高速时钟HCLK ,故当APBx_Prescaler 不为1时,定时器时钟为其2倍,当为1时,为了不超过HCLK ,定时器时钟等于HCLK 。
例如:我们一般配置系统时钟SYSCLK 为168MHz ,内部高速时钟 AHB=168Mhz ,APB1欲分频为4,(因为APB1最高时钟为42Mhz ),那么挂在APB1总线上的timer3时钟为84Mhz 。
《STM32F4xx 中文参考手册》的424~443页列出与通用定时器相关的寄存器一共20个, 以下列出与Timer3相关的寄存器及重要寄存器的简单介绍。
1 TIM3 控制寄存器 1 (TIM3_CR1)SYSCLK(最高AHB_Prescaler APBx_Prescaler作用:1使能自动重载TIM3_ARR2定时器的计数器递增或递减计数。
定时器工作原理及应用引脚图
定时器工作原理及应用引脚图(总8页)-CAL-FENGHAI.-(YICAI)-Company One1-CAL-本页仅作为文档封面,使用请直接删除555定时器摘要:555定时器是一种多用途的数字——模拟混合集成电路,利用它能极方便地构成施密特触发器、单稳态触发器和多谐振荡器。
由于使用灵活、方便,所以555定时器在波形的产生与交换、测量与控制、家用电器、电子玩具等许多领域中都得到了广泛应用。
本文主要介绍了555定时器的工作原理及其在单稳态触发器、多谐振荡器方面的应用。
关键词:数字——模拟混合集成电路;施密特触发器;波形的产生与交换555芯片引脚图及引脚描述555的8脚是集成电路工作电压输入端,电压为5~18V,以UCC表示;从分压器上看出,上比较器A1的5脚接在R1和R2之间,所以5脚的电压固定在2UCC/3上;下比较器A2接在R2与R3之间,A2的同相输入端电位被固定在UCC/3上。
1脚为地。
2脚为触发输入端;3脚为输出端,输出的电平状态受触发器控制,而触发器受上比较器6脚和下比较器2脚的控制。
当触发器接受上比较器A1从R脚输入的高电平时,触发器被置于复位状态,3脚输出低电平;2脚和6脚是互补的,2脚只对低电平起作用,高电平对它不起作用,即电压小于1Ucc/3,此时3脚输出高电平。
6脚为阈值端,只对高电平起作用,低电平对它不起作用,即输入电压大于2 Ucc/3,称高触发端,3脚输出低电平,但有一个先决条件,即2脚电位必须大于1Ucc/3时才有效。
3脚在高电位接近电源电压Ucc,输出电流最大可打200mA。
4脚是复位端,当4脚电位小于0.4V时,不管2、6脚状态如何,输出端3脚都输出低电平。
5脚是控制端。
7脚称放电端,与3脚输出同步,输出电平一致,但7脚并不输出电流,所以3脚称为实高(或低)、7脚称为虚高。
1概述1.1 555定时器的简介555定时器是一种多用途的数字——模拟混合集成电路,利用它能极方便地构成施密特触发器、单稳态触发器和多谐振荡器。
定时器常见实现方式(时间堆、时间轮)
定时器常见实现⽅式(时间堆、时间轮)需求接⼝⾸先需求场景主要有这⼏种(简化):在 n 秒以后执⾏⼀个任务 X每隔 n 秒执⾏⼀次任务 X取消⼀个已经添加的定时器根据上⾯的简化需求,得到需要的主要接⼝:添加⼀个定时器定时器过期执⾏(可能需要重复执⾏)取消⼀个定时器数据结构最后,就是考虑⽤来存放定时器的数据结构(也是定时器设计的核⼼)上⾯的接⼝可以简单的看成这⼏个操作:添加删除查询(获取最近需要执⾏的⼀个)对于这⼏个操作,常⽤的复杂度⽐较均衡的数据结构:红⿊树优先队列(最⼩堆)另外跳表(本质类似红⿊树)也是可⽤的;还有⼀种⽐较巧妙的hash结构时间轮,这是⼀个类似钟表指针的结构,将需要管理的定时器根据时间hash到数组中,以提⾼查询和添加的效率这⼏种实现,现在流⾏的开源框架都各有采⽤,⽐如 Linux 内核⽤的就是时间轮的实现实现下⾯简单实现⼀下采⽤最⼩堆和时间轮作为数据结构的定时器,⽅便起见,编码演⽰使⽤python时间堆所谓的“时间堆”并不是什么稀奇东西,只是⽤⼩堆来管理以时间作为关键字的定时器集合实现思路:使⽤⼀个结构来保存定时器对象的数据,包括过期时间、回调函数等所有的定时器对象保存在⼀个优先队列中(也就是最⼩堆),键值为时间添加定时器即为向队列中插⼊新项删除定时器可以使⽤惰性删除,⾸先给对应的定时器对象置位表⽰其已经取消,当达到⼀定条件时(⽐如取消的定时器数量达到总数量的1/2),进⾏⼀个清理操作按照固定的时间间隔来tick,每次tick都需要看看有没有需要执⾏的定时器对象因为对象由优先队列管理,因此,当队⾸元素不需要执⾏时,后⾯的元素则都不需要执⾏⼀个对象需要执⾏时,可能需要将其删除;也可能是需要重复执⾏的,则需要设定好时间后再次加⼊实现代码如下(仅为演⽰):class Timer():def __init__(self, owner, timestamp, callback, callargs, loop):self.owner: Timers = ownerself.timestamp = timestampself.callback = callbackself.callargs = callargsself.cancelled = Falseself.loop = loopdef __lt__(self, other):return self.timestamp < other.timestamp@propertydef Cancelled(self): return self.cancelled@propertydef Time(self): return self.timestampdef cancel(self):self.cancelled = Trueself.owner.onCancel()def trigger(self, now):if not self.cancelled:self.callback(now, *self.callargs)if self.loop > 0:self.timestamp += self.loopelse:self.cancel()class Timers():def __init__(self):self.timerQue = []self.cancelCount = 0def tick(self):now = int(time.time())self.process(now)def process(self, now):while len(self.timerQue) > 0:timer: Timer = self.timerQue[0]# 到期或取消的定时器需要处理if timer.Time <= now or timer.Cancelled:if not timer.Cancelled:timer.trigger(now)if not timer.Cancelled:heapq.heapreplace(self.timerQue, timer)else:heapq.heappop(self.timerQue)self.cancelCount -= 1else:breakdef onCancel(self):self.cancelCount += 1if self.cancelCount > len(self.timerQue)/2:self.purge()# 这⾥处理的时候参考c++ std partition 操作,先将没⽤的都放到数组最后 # 然后,将前⾯有⽤的重新进⾏⼀次建队操作def purge(self):# patitionl = 0r = len(self.timerQue) - 1while l < r:while l < r and not self.timerQue[l].Cancelled: l += 1while l < r and self.timerQue[r].Cancelled: r -= 1self.timerQue[l], self.timerQue[r] = self.timerQue[r], self.timerQue[l]l += 1r -= 1# removemid = math.ceil((l + r) / 2)if not self.timerQue[mid].Cancelled:mid += 1del self.timerQue[mid:]# heapmakeheapq.heapify(self.timerQue)def add(self, duration, callback, callargs, looptime):now = int(time.time())timer: Timer = Timer(self, now + duration, callback, callargs, looptime)heapq.heappush(self.timerQue, timer)return timer# testdef func(callback):waitEvent = threading.Event()while not waitEvent.wait(1):callback()def func_a(now):print('in func a', now)def func_b(now):print('in func b', now)def func_c(a, b):print('in func c: before sleep')time.sleep(30)print('in func c: after sleep')a.cancel()b.cancel()timers = Timers()threading.Thread(target=func, args=(lambda:timers.tick(), )).start()timer_a = timers.add(0, func_a, (), 10)timer_b = timers.add(5, func_b, (), 10)threading.Thread(target=func_c, args=(timer_a, timer_b)).start()时间轮时间轮是⼀个⽐较巧妙地hash结构,性质有点类似钟表指针⾸先考虑⼀个简单的⼤⼩为60数组t,每⼀位表⽰对应时间的集合:第0秒需要处理的事务都在t[0],第5秒需要处理的事务都在t[5],依次类推这样⼀来,当处在 x 秒时只需要去对应的数组元素即可;但是同样也带来了⼀个问题:最多只能处理⼀分钟内的事务(数组⼤⼩只有60)为了解决这个问题,有⼀个简单的⽅案:在事务对象加⼀个字段 nRound ⽤来表⽰循环使⽤这个数组,当循环次数为 nRound 时才执⾏本事务这样⼀来就解决了数组容量问题,但是随之⽽来的就是效率变得低下了;本来使⽤hash的⽬的就是为了避免不必要的遍历,可以直截了当地获取当前需要处理地任务,⽽现在⼜要遍历 t[n] 来判断 nRound 是否为当前轮次了基于此,就有了这种更优美地解决⽅案时间轮:再加⼀个⼤⼩为60数组d,每⼀位还是表⽰对应时间地集合:第0分钟要处理地事务都在d[0]中,第5分钟需要处理地事务都在d[5]中,依次类推当然,因为0-59秒的事务都放在了数组t⾥了,所以d[0]为空即可;当时间来到第1分钟时,再将d[1]中的事务放置到t中对应位置即可这样⼀来,就已经可以处理1个⼩时内的任务了,可想⽽知,再加上更多的数组就可以处理更长的时间跨度了(很明显,这⾥的数组⼤⼩和数组数量只是⼀个进制关系⽽已)⼯作原理如下:⼀级轮(就是⼀个数组)的每个格对应⼀个时间间隔⼀级轮指针每次tick加1;当指针指向⼀级轮的某⼀格时,即表⽰这⼀格⾥的定时器都到期了⼆级轮(包括更多级同理)的每个格对应⼀级轮的⼀圈⼆级轮指针每当⼀级轮指针转⼀圈加1;当指针指向某⼀个时,即表⽰接下来需要处理这⼀格的定时器了(分散到⼀级轮⾥)后⾯给出了⼀个简单的实现,简单起见就没有再处理重复执⾏的情况这⾥再谈谈⼀些可以优化的地⽅:指针和数组⼤⼩:在Linux内核定时器的实现⾥,采⽤了⼀个很巧妙的设计,⼀共5个数组,⼤⼩分别为 64(a) 64(b) 64(c) 64(d) 255(e) (64是2的6次⽅,255是2的8次⽅,也就总共占了32位);这样⼀来,只需利⽤整数的进位就可以⾃然的处理数组之间的进制关系了,这体现在,只需要⼀个32bit的指针和对应的位操作即可表⽰5个数组中的情况了,不再需要每个数组分配⼀个指针(例如末8位表⽰数组e的指针,之前的6位表⽰数组d的指针)数组元素:更⾼级的数组对应的时间刻度就越长,就看可能有更多甚⾄⾮常⼤量的事务挤在⼀个格⾥,这时候特殊需求的操作(⽐如查询),可能就不能很好地⽀持,因此可以采⽤合适的数据结构来管理每个格⾥的事务;当然通常情况是不需要的,因为⼀般操作只是要把当前格⾥的事务hash到下⼀级的数组⾥取消定时器:还是跟上⾯时间轮的实现⼀样,可以考虑采⽤惰性删除的策略class Timer():def __init__(self, delay, callback, callargs):self.delay = delayself.callback = callbackself.callargs = callargsself.cancelled = False@propertydef Cancelled(self): return self.cancelleddef cancel(self):self.cancelled = Truedef trigger(self):if not self.cancelled:self.callback(*self.callargs)self.cancelled = Trueclass Wheels():WHEEL_NUM = 3SOLT_NUM = 8MAX_NUM = int(math.pow(SOLT_NUM, WHEEL_NUM))def __init__(self):self.pointer = []self.wheels = []for i in range(self.WHEEL_NUM):self.pointer.append(0)self.wheels.append([])for j in range(self.SOLT_NUM):self.wheels[i].append([])def add(self, delay, func, args):if delay >= self.MAX_NUM: returnpast = delaywheel = self.WHEEL_NUM - 1while past >= self.SOLT_NUM:past = past // self.SOLT_NUMwheel -= 1solt = (self.pointer[wheel] + past) % self.SOLT_NUMdelay = delay % int(math.pow(self.SOLT_NUM, self.WHEEL_NUM - wheel - 1))self.wheels[wheel][solt].append(Timer(delay, func, args))def tick(self):print('tick')for i in range(self.WHEEL_NUM - 1):while self.wheels[i][self.pointer[i]]:timer = self.wheels[i][self.pointer[i]].pop()self.add(timer.delay, timer.callback, timer.callargs)idx = self.WHEEL_NUM - 1while self.wheels[idx][self.pointer[idx]]:timer = self.wheels[idx][self.pointer[idx]].pop()timer.trigger()for i in range(self.WHEEL_NUM - 1, -1, -1):self.pointer[i] = (self.pointer[i] + 1) % self.SOLT_NUMif self.pointer[i] > 0:breakdef func(callback):waitEvent = threading.Event()while not waitEvent.wait(1):callback()wheels = Wheels()threading.Thread(target=func, args=(lambda:wheels.tick(), )).start()# testdef func_a():print('in func a')def func_b():print('in func b')timer_a = wheels.add(5, func_a, ())timer_b = wheels.add(10, func_b, ())总结到这⾥,关于定时器的实现就讲完了;实现本⾝可能并不精彩,秒的是⼀些细节处的设计思想,最后再来回顾⼀下:惰性删除和清理操作:参考了开源引擎kbengine中定时器的实现;惰性删除可以节省⾼频操作的开销,还可以减少频繁的内存操作;在清理操作的细节中,先将有⽤和⽆⽤的定时器分离(分别放置到数组前后),可以有效提⾼内存操作的效率时间轮结构的设计:时间轮结构的设计⾮常巧妙,源于hash也兼顾了空间,并且将⾼频操作(最近时间段)和低频操作(较远时间段)分离了开来时间轮指针设计:利⽤整数进位,简化时间轮指针。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
【原创】技术系列之定时器(二)...
【原创】技术系列之定时器(二)
作者:CppExplore 网址:/CppExplore/ 一、上篇文章描述。
文章《定时器(一)》
/CppExplore/archive/2008/04/02/46111 .html实现了一个定时器模块,这个实现每次延时时间到都要扫描所有的定时器对象,效率低下。
开始设想的时候,LIST 中的定时器对象保存间隔时间段的毫秒值,导致每次延时时间到都要做“时间减少操作”直到减少到零,并且得出不需排序的结论。
二、改进。
如果其中保存超时的精确时间点,而不是保存时间段,则可以在LIST中根据超时时间点对定时器对象排序,延时时间到,则从链表头扫描定时器对象,取其超时时间点与当前时间点对比,如果小于等于当前时间点,则进行超时处理,否则终止继续扫描,避免不必要的扫描操作。
同时插入对象的时候插入到合适的位置,以保持链表的顺序化。
三、主要数据结构。
此次容器结构选择内核数据结构中的TAILQ,因为LIST没有插入尾部操作(当要插入的定时器对象超时时间点大于所有队列中的对象的时候)。
四、新的时间类型操作。
另一方面很多地方涉及到对struct
timeval结构的操作,这里介绍几个对该结构进行操作的宏,都已经在系统头文件中定义,可以使用函数原型的方式理解就是如下:
timeradd(struct timeval *p1,struct timeval *p2,struct timeval
*result);
timersub(struct timeval *p1,struct timeval *p2,struct timeval
*result);
timercmp(struct timeval *p1,struct timeval *p2,operator op);
对struct timespec同样有
timespecadd/timespecsub/timespeccmp,另外还有两者的转换宏,使用函数原型的方式理解就是:
TIMEV AL_TO_TIMESPEC(struct timeval *p1,struct timespec *result);
TIMESPEC_TO_TIMEV AL(struct timespec *p1,struct timeval *result);
如果系统的头文件中找不到,也可以自己实现下,明白这两个结构的细节结构,实现也很简单,这里拿timeradd举例,其它不说了。
#define timeradd(tvp, uvp, vvp)
\
do { \
(vvp)->tv_sec = (tvp)->tv_sec +
(uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec +
(uvp)->tv_usec; \
if ((vvp)->tv_usec >= 1000000)
{ \
(vvp)->tv_sec++; \
(vvp)->tv_usec -= 1000000; \ } \
} while (0)
五、实现。
和上篇文章相比,改动比较的在add_timer_和process方法。
当前add_timer_操作需要顺序扫描插入到合适的位置以保持链表的顺序。
当前process的主要代码如下:while(manager->m_state==TIMER_MANAGER_START){ _sec=manager->m__sec;
_usec=manager->m__usec;
while(select(0,0,0,0,&tm)<0&&errno==EINT R);
gettimeofday(&now,0);
/**//*加上误差补偿时间*/
timeradd(&now,&manager-
>m_repair,&stand);
pthread_mutex_lock(&manager->m_mutex);
TAILQ_FOREACH(item, &(manager->list_), entry_){
/**//*取修正后的时间逐个和定时器中的超时时间点相比,遇到不超时对象则退出扫描*/
if(timercmp(&item->m_endtime,&stand,<)){
if(item->m_func)
item->m_func(item,item->m_data);
if(item->m_type==CTimer::TIMER_ONCE){
manager->remove_timer_(item);
item->m_state=CTimer::TIMER_TIMEOUT;
}
else
if(item->m_type==CTimer::TIMER_CIRCLE){
/**//*循环性的要保证链表的顺序性,如要重新插入,保存entry_的原因,是执行新的插入后不要影响当前进行的循环*/
tmpTimer.entry_=item->entry_;
manager->remove_timer_(item);
manager->add_timer_(item);
item=&tmpTimer;}
}
else break;
}
pthread_mutex_unlock(&manager->m_mutex);
}
六、源码
写了个简单的makefile,执行make就可以生成测试程序:test,执行./test可以看运行效果。
make由make so和make test组成,make so在lib目录下生成libtimer.a。
make clean清空。
源代码下载这里:
/Files/CppExplore/timer.tar.gz
七、后记
与上一版本相比,该文中的定时器实现要求在定时器模块运行期间不能修改系统实际,上一版本实现则无此限制。
定时器模块的锁初始化为fastmutex方式,因此在回调函数里注意不能再调用CTimer的start stop reset等方法,以免死锁,如果有调用的需求,可以把锁修改为循环锁recmutex方式。
2008/4/8补记:本文是timer的v2实现,定时器timer在业务线程中执行start的时候,要执行扫描排序操作,导致返回时间延长。
新的v3版本实现,定时器timer的start操作不再执行扫描操作,而是简单插入队列头同时置一变量表示尚未排序。
定时器线程延迟时间到,首先扫描未排序对象,执行排序(从尾部逆向对比,根据实际使用,越晚插入的对象,超时的时间点越靠后,逆向则尽可能减少对比操作),再扫描判断是否超时。
详细的代码不发了,如果需要,可以发邮件索取。
修改的出发点:尽量减少定时器对象操作对业务线程的影响。
posted on 2008-04-03 21:49 cppexplore 阅读(1755) 评论(1) 编辑收藏引用
评论
# re: 【原创】技术系列之定时器(二)2008-09-16 13:41 zam 很好,能否发一个V3版本的源码给我,非常感谢!
我的E-mail: zam1208@ 回复更多评论。