关于51单片机定时器的灵活使用

发布者:数字驿站最新更新时间:2016-05-16 来源: eefocus关键字:51单片机  定时器  计数器 手机看文章 扫描二维码
随时随地手机看文章
前段时间,做一个项目,有串口收发指令判断,按键类型判断,长短按之类,power的定时关电,事件的轮询扫描更新和display的定时扫描。这些要求就对定时器提出了要求,但是我的51单片机只有两个定时器,其中一个又有debug口的波特率产生之用。于是乎我可以用的定时器就只剩下了一个。怎么办?可能大家都只能用变量在定时中断函数去做多了任务就行了,但是我总是感觉这样会导致代码看起来太不具有条理性,而且对于日后的管理不是很容易。思来想去,就想到了linux内核中对于定时器的封装,那种面向对象的思想。

 

想法有了,我觉得实现就是很简单了。下面给大家贴上我的代码:

.h 文件: 

 

#ifndef __SC_TIMER_H
#define __SC_TIMER_H

/* SC_Common.h文件中包含了对数据类型的定义和包含对应的单片机的配置头文件,这里就没有列出,根据个人所使用情况而定 */
#include "SC_Common.h"

#ifdef MODE1T
#define T0TIMES	(65536-FOSC/1000)
#define T1TIMES	(65536-10*FOSC/1000)
#else
#define T0TIMES	(65536-FOSC/12/1000)			// 0.1ms
#define T1TIMES	(65536-10*FOSC/12/1000)			// 10ms
#endif /*MODE1T*/

#define TIMER_SIZE	4

typedef struct
{
	U8 timerId;			/* 定时器的id,实则指明了起所在数组中的位置 */
	S8 isRuning;			/* 表明当前timer是否正在运行 */
	U16 curTimes;			/* 当前timer时间 */
	U16 expireTimes;		/* 当前timer的溢出时间 */
	U8 existInArry;		        /* 当前的timer是否存在于数组之中 */
	TimerFunc timerFunc;	        /* 当前timer的指定运行函数 */
} Timer;

void InitTimer(void);
S8 AddTimer(Timer *timer);
S8 DelTimer(Timer *timer);
S8 StartTimer(Timer *timer);
S8 ModifyTimer(Timer *timer);
S8 StopTimer(Timer *timer);
S8 IsRunningTimer(Timer *timer);

#endif	/*__SC_TIMER_H*/

 

下面是这部分思想的重点实现,无非就是向timerArray数组中添加删除定时器,简言之,即所谓的增删改查,还有就是启动停止定时器,考虑到51单片机的性能,没有像linux内核中那样用链表实现,同时定时器的总数也是有上限要求的。

.c文件:

#include "SC_Timer.h"

#include 
#include 

/* 这里采用数组的方式管理各个timer结构体 */
Timer timerArray[TIMER_SIZE];
U8 timerUsed = 0;

void InitTimer(void)
{
	TMOD |= 0x01;
	TL0 = T0TIMES;
	TH0 = T0TIMES >> 8;
	ET0 = 1;
	TR0 = 1;

	timerUsed = 0;
	memset(timerArray, 0, sizeof(timerArray));
}

S8 AddTimer(Timer *timer)
{
	if(timerUsed >= TIMER_SIZE)
		timerUsed = 0;

	/*×Ô¶šÒåtimerIdµÄÉú³É·œÊœ£¬ŒŽŽú±íÆäÔÚÊý×éÖеÄλÖÃ*/
	timer->timerId = timerUsed;
	timerArray[timerUsed] = *timer;
	timerUsed++;

	timer->existInArry = 1;
	timer->isRuning = 0;

	printf("%bu\n", timer->timerId);
	return 0;
}

static void Del_Timer(Timer *timerArray, U8 *timerUsed, U8 pos)
{
	U8  i = 0;
	U8 len = *timerUsed;

	for(i=pos; itimerId >= TIMER_SIZE || timer->timerId < 0 || timer->existInArry == 0)
		return -1;

	if(timerUsed <= 0)
		return -1;
	
	Del_Timer(timerArray, &timerUsed, timer->timerId);

	timer->existInArry = 0;

	return 0;
}

S8 StartTimer(Timer *timer)
{
	if(timer->timerId >= TIMER_SIZE || timer->timerId < 0 || timer->existInArry == 0)
		return -1;

	timerArray[timer->timerId].isRuning = 1;

	return 0;
}

S8 ModifyTimer(Timer *timer)
{
	if(timer->timerId >= TIMER_SIZE || timer->timerId < 0 || timer->existInArry == 0)
		return -1;

	timerArray[timer->timerId].curTimes = timer->curTimes;
	timerArray[timer->timerId].expireTimes = timer->expireTimes;
	timerArray[timer->timerId].timerFunc = timer->timerFunc;

	return 0;	
}

S8 StopTimer(Timer *timer)
{
	if(timer->timerId >= TIMER_SIZE || timer->timerId < 0  || timer->existInArry == 0)
		return -1;

	timerArray[timer->timerId].isRuning = 0;

	return 0;
}

S8 IsRunningTimer(Timer *timer)
{
	S8 ret = -1;
	
	if(timer->timerId >= TIMER_SIZE || timer->timerId < 0 || timer->existInArry == 0)
		return ret;

	ret = timerArray[timer->timerId].isRuning;

	return ret;
}

/* 
 *  定时器的中断函数负责判断各个事件的时间是否到达,如果到达调用相应的相应函数进行运行
 *  由于51单片机的函数指针是没有堆栈保护的,所以这里加入了汇编指令执行堆栈的保护,个人
 *  水平有限,这里希望大家指正是否有错误之处,谢谢
 */
void Tm0Isr(void) interrupt 1
{	
	U8 i = 0;

	TL0 = T0TIMES;
	TH0 = T0TIMES >> 8;

	for(i=0; i= timerArray[i].expireTimes)
			{
				#pragma asm
				push ACC
				push DPH
				push DPL
				#pragma endasm
				(*timerArray[i].timerFunc)();
				#pragma asm
				pop DPL
				pop DPH
				pop ACC
				#pragma endasm
				timerArray[i].curTimes = 0;
			}
		}
	}
}

 

本文中的数据类型都是通过typedef转化过的,为了时时刻刻关于自己的内存使用量,,定义如下

typedef unsigned char U8;
typedef unsigned short int U16;
typedef unsigned long int U32;
typedef signed char S8;
typedef signed short int S16;
typedef signed long int S32;
typedef bit BOOL;

 

 个人认为这个对于项目后面的能够有效快速的进行起到了很大的帮助。   典型的用法如下:

 

	Timer pressKeyTimer;        /* 这里的timer请使用全局变量,大家应该懂的,就是变量的生命周期的问题啦 */
 pressKeyTimer.curTimes	  = 0;
	pressKeyTimer.expireTimes = 11;
	pressKeyTimer.timerFunc   = JudgeKeyType;
	AddTimer(&pressKeyTimer);
 StartTimer(&pressKeyTimer);

至此,到规定的时间11msec时,就会调用这里的JudeKeyType函数,进行轮询发现是否有按键按下,并判断其类型。

 

望有改进意见,谢谢高手指正。

关键字:51单片机  定时器  计数器 引用地址:关于51单片机定时器的灵活使用

上一篇:51单片机定时器量程的硬件扩展方式
下一篇:51单片机定时器初值的计算

推荐阅读最新更新时间:2024-03-16 14:53

51单片机中什么是bit和sbit?_bit和sbit区别
回顾C语言发现在单片机中有bit sbit sfr 等一些类型! 问题:什么是bit和sbit?他们有什么区别?   bit : 编译时分配空间 sbit 只能在外部定义全局变量。 sfr(特殊功能寄存器)的bit。SFR是系统指定的内存地址。   bit 动态分配的,有编译器来指定内存地址。   bit和sbit都是C51扩展的变量类型。   sbit 要在最外面定义,就是说必须定义成外部变量.sbit定义的是SFR(特殊功能寄存器)的bit   sbit更像是类型定义,不像是变量定义。   sbit: 指示说明性说明   bit 可以在外部或内部定义。
[单片机]
51单片机做的智能时钟具有闹钟功能(DS1302+DS18B20+LCD1602)
本贴针对学完单片机并且有读懂代码的非新手同学。本人目前放寒假,这个是本人在上个学期的单片机课上要求做的综合实验,现在重新修改了下增加了菜单目前测试毫无问题可以完美使用。有志向做闹钟的同学可以参考一下,原码上由本人写的大量注释可以方便看懂。 使用了LCD1602、DS1302、DS18B20用来测试温度、内部含闹钟系统但本人没有做EEPROM有需要的同学可以自行添加。 单片机源程序如下: #include reg52.h #include key.h #include LCD1602.h #include DS1302.h #include music.h #include DS18B20.H #define no
[单片机]
动态扫描方法和定时器1在数码管的前三位显示出秒表
/* 用动态扫描方法和定时器1在数码管的前三位显示出秒表 */ #include reg52.h #include intrins.h #define uint unsigned int #define uchar unsigned char uint closedu,closewe; uchar code wetable ={ 0xfe,0xfd,0xfb,0xf7, 0xef,0xdf,0xbf,0x7f}; uchar code dutable ={ 0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71}
[单片机]
51单片机内部资源
通过这篇博客对这一段时间对51单片机的学习做一定总结,这是对单片机内部资源的一定总结:单片机的内部资源总体上分为两部分:基本功能和服务性功能。如下图所示; 对于基本的器件有:LED灯,蜂鸣器、继电器、步进电机和按键。其中通过LED的不同组合方式可以扩展出LED流水灯、数码管和点阵。其实基本的器件的工作方式是基本相同的,只是对电平表现出的不同输出方式。其中数码管可以通过动态和静态两种方式显示;静态显示为一对一形式,动态显示为一对多形式的循环显示。其中点阵的表现方式其实和数码管的动态显示是相同的,只是点阵和数码管的LED灯的排列方式不同而已。对于X*Y形式的按键扫描方法是:行列交叉取点发。 上述都为一些基本器件,为了方便和更加
[单片机]
NRF52840学习历程(六)RTC 实时计数器(滴答定时器)
开发板:初雪的100出头那块 NRF52840 EVAL KIT 下载工具:JINLK V11(最好是JLINK V9以上 也有人用JLINK OB也行,其他的下载器诸如STLINK,DAP不建议用) 版本号: KEIL5编程环境,CMSIS为5.3.0, NRF52840的CMSIS为8.35.0 参考资料: NRF52840-Eval-Kit-Schematic.pdf(原理图) nRF5_SDK_17.0.2_d674dde(官方例程) nRF5_SDK_17.0.0_offline_doc(官方文档) nRF52840_PS_v1.1.pdf(官方数据手册) RTC 实时计数器 32.768KH
[单片机]
NRF52840学习历程(六)RTC 实时<font color='red'>计数器</font>(滴答<font color='red'>定时器</font>)
51单片机I/O口使用经验
按常规,在51端口(P1、P2、P3)某位用作输入时,必须先向对应的锁存器写入1,使FET截止。一般情况是这样,也有例外。所谓IO口内部与电源相连的上拉电阻而非一常规线性电阻,实质上,该电阻是由两个场效应管并联在一起:一个FET为负载管,其阻值固定;另一个FET可工作在导通或截止两种状态(姑且叫可变FET)。使其总电阻值变化近似为0或阻值较大(20千欧--40千欧)两种情况。当和端口锁存器相连的FET由导通至截止时,该阻值近似为0,可将引脚快速上拉至高电平;当和锁存器相连的FET由截止至导通时,该电阻呈现较大阻值,限制了和端口锁存器相连的FET的导通电流。 51I/O口作为输入端和外部信号相连有时必须考虑上述特性,本人在设计LT
[单片机]
51单片机-按键部分(3)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 1
[单片机]
c51中定时器工作时如何使用T0脚输入脉冲信号?
嵌入式系统中,定时器是一种常用的设备,可以实现各种时间控制功能,如计时、计数、产生中断等。8051单片机中有两个定时器:T0和T1,其中T0定时器主要用于计时和计数操作,可以通过T0脚输入脉冲信号来控制定时器的工作。 T0脚是单片机的P3.4口,既可以作为普通的输入输出口,也可以作为T0定时器的外部输入引脚。当T0脚作为定时器的外部输入引脚时,可以通过输入的脉冲信号来控制定时器的计数。在使用T0脚输入脉冲信号时,需要采取一些措施来确保信号的稳定性和正确性。 首先,需要确定输入脉冲信号的频率。在使用T0脚输入脉冲信号时,需要将信号的频率与定时器的工作频率相匹配。具体来说,当定时器使用外部时钟源时,其工作频率为外部时钟源频率的一半,因
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved