单片机软定时

2018-11-04 01:44:06 qq_39792671 阅读数 571
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
uchar code table[] = {0x01, 0x02, 0x04, 0x08};
uchar code ledCode[]= {0xc0,0xf9,0xa4,
	0xb0,0x99,0x92,0x82,0xf8,0x80,
0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
int t0 = 0;
uchar count = 0;
sbit LED = P0^0;
void delay(int z)
{
	int i, j;
	for (i=0; i<z; i++)
		for (j=0; j<110; j++);
}
int main(void)
{
	int i = 0;
	LED = 0;

	TMOD = 0x01;
	TH0 = (65536 - 30)/256;;
	TL0 = (65536 - 30)%256;;
	EA = 1;
	ET0 = 1;
	TR0 = 1;
	P2 = 0xff;		//数码管位控制端
	P1 = ledCode[t0];	//数码管段控制
	
	while(1)
	{
		if (t0 == 20)
		{
			t0 = 0;
			P1 = ledCode[count++];
			if (count == 10)
				count = 0;	
		}
	}
	return 0;
}

void timer0() interrupt 1
{
	TH0 = (65536 - 30)/256;
	TL0 = (65536 - 30)%256;
	delay(1000);
	LED = ~LED;
	t0++;
}

如果在定时函数中没有延时函数的话,那么程序应该是LED灯50ms闪烁一次,数码管从0-9变化。
在这个程序当中,首先是定时器装入初值,然后计时开始,执行到if不断判断,然后定时器溢出TF0=1,程序检测到TF0 = 1,进入定时函数,装入初值,进入延时函数,延时结束后,回到定时函数,在延时函数中,不会被定时打断,但是在延时中,TF0 = 1,然后执行剩余的定时函数,回到主函数中,检测到TF0 = 1,进入定时函数,不断循环。
总的来说,执行main函数被打断(TF=1),进入timer0,进入delay,TF0 = 1,但是delay不会被打断,然后回到timer0,再回到main,检测到TF0 = 1,再次进入timer0。

main()timer0()delay()检测到中断信号TF0=1进入延时子函数延时期间TF0=1,但不打断延时程序回到主函数程序检测到TF0 = 1main()timer0()delay()

有错误,请指正
有错误请指正

单片机中断处理时间大于定时器定时时间,在下次中断时间到时,因为中断是同一类型、同一优先级,所以不会马上进入新的中断处理。
而是在本次中断处理结束后,单片机又马上进入新的定时器中断函数,主函数中的语句可能会没有机会运行下去,会影响后面中断的实时性。

如果为了避免中断嵌套(同一优先级不会发生),在中断处理中人为的在进入中断时关中断,处理完中断后开中断,会影响后面中断的实时性。

最安全的处理方式:中断处理时间越短越好,有什么事放外面处理。中断的初衷是为了提高系统实时性,中断处理太长,会影响同级的中断响应,其它中断谈何实时。

2016-10-16 13:20:32 akamod 阅读数 0

一直想在单片机上实现一个像MFC和WPF一样的软定时器,花了点时间终于写出来了,使用时需把timer_run函数放到定时器中断服务函数里面

typedef struct _TIMER
{
	unsigned char isEnable;
	unsigned int  count;
	unsigned int  countNeed;
	void (*func)();	
}TIMER;



void timer_Run(TIMER *timer)
{
	if(timer->isEnable)
	{
		timer->count ++;
		if(timer->count == timer->countNeed)
		{
			timer->count = 0;
			timer->func();
		}
	}	
}

void startTimer(TIMER *timer)
{
	timer->count = 0;
	timer->isEnable = 1;
}

void stopTimer(TIMER *timer)
{
	timer->isEnable = 0;
	timer->count =0;
}

2012-11-16 19:50:49 fuwenwei 阅读数 600

标题似乎写得不大合适,不妨设想一个这样的需求场景:单片机程序一般有个大循环while(1),

在这个循环中处理所有中断以外的事情,这些事情需要被执行的周期各不一样,有些需要频繁地做

,有些需要很长时间才做一次。我们把这些事情记为T1,T5,T10 ,T200...

T表示task或者time吧,后面的数字表示执行周期,即T1表示此事情1秒执行一次,T2表示需要

2秒执行一次。。。碰到这种需求,我们会想要是有ucos就好了,要是有Linux就好了。

好在情况不是很复杂,事情需要的执行周期也不是很精准,任务之间的交互基本上也没有。

这里给出一个实现这样需求的模板:

首先需要一个定时器,单片机的硬件定时器,并使能中断,中断中只做两件件事:

uint8_t  tick=0;

void  timer_int(void)
{
       1:重装初值 10ms

       2:tick++;
}

在大循环中:

void main()

{

    sys_init();

    while(1){

           tasks();

    }

}

真正的工作在tacks中:

static uint8_t timer_1s = 0;
static uint8_t timer_5s   = 5;
static uint8_t timer_10s  = 2;
static uint8_t timer_100s  = 10;
void tasks()

{  

if(timer_1s < 100){
  return;
 } 

disable_global_interrupts();
 timer_1s = 0;
 enable_global_interrupts();

//------- process per 1s --------//

tsk_1s(); 

sw_timer_dec(timer_5s );

if(timer_5s > 0){
  return;
 } 

timer_1s = 5;

//------- process per 5s --------//

tsk_5s(); 

sw_timer_dec(timer_10s );

if(timer_10s > 0){
  return;
 } 

timer_10s = 2;

//------- process per 5s --------//

tsk_10s();

 

......

}

 

void sw_timer_dec(uint8_t* timer)
{
 if(*timer == 0){
  return;
 }
 (*timer)--;
}

 

 

2018-06-22 19:28:00 weixin_33757911 阅读数 0
  • 之前写了一个软件定时器,发现不够优化,和友好,现在重写了
  • soft_timer.h
#ifndef _SOFT_TIMER_H_
#define _SOFT_TIMER_H_
#include "sys.h"


typedef void (*timer_callback_function)(void*);



typedef enum {
    TIMER_ENABLE,
    TIMER_DISABLE
}SoftTimerStatus;

typedef enum {
    TIMER_MALLOC_ERR=-1,
    TIMER_PRARM_ERR=-2,
    TIMER_OK=-3

}SoftTimerReturn;

typedef enum {
    /*立即调用一次*/
    IMMEDIATE_CALL_MODE,
    /*等待定时器时间到后调用*/
    WAIT_CALL_MODE
}SoftTimerMode;
typedef struct SOFT_TIMER{

    timer_callback_function timerFunc;
    SoftTimerStatus sTimerStatus;
    SoftTimerMode sTimeMode;
    u32 timerStartTime;
    u32 timerTime;
    u16 timerIndex;
    /*指向下一个定时器*/
    struct SOFT_TIMER* nextTimer;
    /*指向前一个定时器*/
    struct SOFT_TIMER* frontTimer;

}soft_Timer;
void soft_TimerInit(void);
SoftTimerReturn soft_TimerCreate(soft_Timer** timer,timer_callback_function fn,SoftTimerMode mode,SoftTimerStatus status,u32 time,u16 timer_index);
SoftTimerReturn soft_TimerDelete(soft_Timer* timer);
void soft_TimerTickUpdate(void);
void soft_TimerTask(void);
u32 soft_TimerTime(void);
void soft_TimerCmd(soft_Timer* timer,SoftTimerStatus status);
u16 soft_TimerNum(void);
void soft_TimerTest(void);


#endif // _SOFT_TIMER_H_

  • soft_timer.c
#include "soft_timer.h"
#include "stdlib.h"
#include "string.h"
#include "stdio.h"

//#define NULL 0

u16 softTimerNum=0;
soft_Timer* TimerHead=NULL;
/*系统时间单位ms*/
u32 system_time=0;

void soft_TimerInit(void)
{
    soft_Timer* tempTimer=NULL;
		while(TimerHead!=NULL)
		{
				tempTimer=TimerHead;
				TimerHead=TimerHead->nextTimer;
				free(tempTimer);
				tempTimer=NULL;
		}
}
SoftTimerReturn soft_TimerCreate(soft_Timer** timer,timer_callback_function fn,SoftTimerMode mode,SoftTimerStatus status,u32 time,u16 timer_index)
{
	soft_Timer* tempTimer;
	
	tempTimer=TimerHead;
	/*为定时器分配空间*/
	TimerHead=(soft_Timer*)malloc(sizeof(soft_Timer));
	/*分配空间失败*/
  if(TimerHead==NULL)return TIMER_MALLOC_ERR;
	/*定时器模式*/
	TimerHead->sTimeMode=mode;
	/*定时器功能*/
	TimerHead->timerFunc=fn;
	/*定时器索引号*/
	TimerHead->timerIndex=timer_index;
	/*定时器时间*/
	TimerHead->timerTime=time;
	/*定时器状态*/
	TimerHead->sTimerStatus=status;
	/*下一个定时器*/
	TimerHead->nextTimer=tempTimer;
	/*前一个定时器*/	
	if(tempTimer!=NULL)
	tempTimer->frontTimer=TimerHead;
	/*前一个定时器*/

	TimerHead->frontTimer=NULL;
	/*定时器启动时间*/
	if(mode==IMMEDIATE_CALL_MODE)
			TimerHead->timerStartTime=system_time-time;
	else
			TimerHead->timerStartTime=system_time;
	/*定时器数*/
	softTimerNum++;
	/*返回定时器指针*/
	*timer=TimerHead;
	return TIMER_OK;
	
}

SoftTimerReturn soft_TimerDelete(soft_Timer* timer)
{	
		/*参数错误*/
    if(timer==NULL)return TIMER_PRARM_ERR;
		if(timer==TimerHead)
		{
			TimerHead=TimerHead->nextTimer;
			if(TimerHead!=NULL)
				TimerHead->frontTimer=NULL;
			free(timer);
			timer=NULL;
		}
		else
		{
			if(timer->nextTimer==NULL)
			{
				timer->frontTimer->nextTimer=NULL;
				free(timer);
				timer=NULL;
			}
			else
			{
				timer->frontTimer->nextTimer=timer->nextTimer;
				timer->nextTimer->frontTimer=timer->frontTimer;
				free(timer);
				timer=NULL;
			}
		}
    softTimerNum--;
    return TIMER_OK;
}
void soft_TimerTickUpdate(void)
{
    system_time++;
}
u16 soft_TimerNum(void)
{
    return softTimerNum;
}
void soft_TimerTask(void)
{
    soft_Timer* tempTimer;
    tempTimer=TimerHead;
    while(tempTimer!=NULL)
    {
        /*定时器使能状态*/
        if(tempTimer->sTimerStatus==TIMER_ENABLE)
        {
            /*计时是否达到*/
            if((system_time-tempTimer->timerStartTime)>=tempTimer->timerTime)
            {
                /*执行回调函数*/
                tempTimer->timerFunc(tempTimer);
                /*更新起始时间*/
                tempTimer->timerStartTime=system_time;
            }
        }
        tempTimer=tempTimer->nextTimer;
    }
}
u32 soft_TimerTime(void)
{
    return system_time;
}
void soft_TimerCmd(soft_Timer* timer,SoftTimerStatus status)
{
	timer->sTimerStatus=status;
}
void callfun(void* arg)
{
    static u32 cnt=0;
    soft_Timer* st;
    st=(soft_Timer*)arg;
//    printf("this is a soft timer test %d.\r\n",cnt++);
//    printf("timer index is %d\r\ntimer time is %d\r\n timer mode is %d\r\ntimer num is %d\r\n",st->timerIndex,st->timerTime,st->sTimeMode,soft_TimerNum());
}

void soft_TimerTest(void)
{
    soft_Timer* st;
		soft_Timer* st1;
	
    soft_TimerInit();

		soft_TimerCreate(&st,callfun,IMMEDIATE_CALL_MODE,TIMER_ENABLE,1000,4);
		soft_TimerCreate(&st1,callfun,IMMEDIATE_CALL_MODE,TIMER_ENABLE,1000,4);
		soft_TimerCreate(&st,callfun,IMMEDIATE_CALL_MODE,TIMER_ENABLE,1000,4);
	
		soft_TimerDelete(st1);
	
    while(1)
    {
			
        soft_TimerTickUpdate();
        soft_TimerTask();
			
    }
}


转载于:https://my.oschina.net/u/2345008/blog/1834300

2016-05-30 22:45:39 zhangren_ham 阅读数 2508

单片机软件定时器的使用方法

特别声明:文章是原创但是本文讲述的思想是在国外的开源代码中借鉴的

初学者在编写单片机程序时经常会用到延时函数,但是当系统逐步复杂以后(没有复杂到使用操作系统)延时会因为延时降低MCU的利用率,更严重的会影响系统中的“并行”操作例如一个既有按键又有蜂鸣器的系统中,如果要求按下按键发出不同的声音,每次发声时间在1秒-2秒之间, 如果用延时来做代码很简单:

//蜂鸣器发出“哔-哔-哔”声音时间约1s
void BeepFuction(void)
{
  unsigned char i;
  for(i=0;i<3;i=++)
  {
    BeepEn(); //开启蜂鸣器
    Delayms(220);//延时220ms
    BeepDis();//关闭蜂鸣器  
    Delayms(110);//延时110ms  
  }
}

当这段代码执行时MCU不可能同时处理按键检查程序因为它大部分时间在执行Delayms()函数中的nop指令,这样就不可能去执行检查按键了(不使用中断时),如果把程序改成流程形式的写法则结果会大为不同,下面先介绍一下基本原理。

我们都知道一般的定时器为16位或8位循环计数,例如对于16位的计数器当计数器数值从0增加到65535时再加一就会回到0那么我们来比较下面两种情况(不考虑计数器在记录当前时刻T后再次回到或超过T这种情况我暂且称它为“压圈”):
情况1:
T1时刻计数器数值为300
T2时刻计数器数值为400
则T1时刻到T2为100个计数单位。
这段时间差也为100个计数单位。
情况2:
T1时刻计数器数值为65535
T2时刻计数器数值为99
则T1到T2 可以算出为65535到0的1个计数单位再加上 0到99的99个计数单位总共为100个计数单位。
所以时间差还是100个计数单位。
在C语言中如果使用两个无符号数作减法会得到如下结果:99-65535=100,这个很好理解就和10进制的借位一样只不过借位后不用管高位了也就相当于99+65536-65535结果是100了,当然这些前提条件都是计数器不会出现“压圈”。
有了上面对定时器的了解就可以从新写这个Beep函数了

//蜂鸣器发出“哔-哔-哔”声音时间约1s
bit BeepFlag = 0;//蜂鸣流程忙标志位
bit BeepCtrl = 0;//蜂鸣器流程控制标志位
void BeepProc(void)
{
  static unsigned int BeepTimer;
  static unsigned char BeepStatus = 0;
  static unsigned char i;
  switch(BeepStatus)
  {
    case 0://
       if(BeepCtrl)
       {
        i = 3;//蜂鸣次数
        BeepFlag = 1;//置位忙标志位
        BeepCtrl = 0;//清除控制标志位
        BeepTimer = TIMER;//这里TIMER为系统定时器计数时钟为1ms
        BeepEn(); //开启蜂鸣器
        BeepStatus = 1;//进入下一个状态
       }
    break;
    case 1://蜂鸣状态
       if(TIMER-BeepTimer>220)//220ms
       {
         BeepDis(); //关闭蜂鸣器
         BeepTimer = TIMER;//记录时刻
         BeepStatus = 2;//进入下一个状态
       }
    break;
    case 2://停止蜂鸣状态
       if(TIMER-BeepTimer>110)//110ms
       {
         if(i!=0)
         {
           i--;
           BeepTimer = TIMER;//记录时刻
           BeepEn(); //开启蜂鸣器
           BeepStatus = 2;//回到蜂鸣状态
         }
         else
         {
           BeepStatus = 0;//回到初始状态
           BeepFlag = 0;//清除忙标志位
         }
       }    
    break;
    default:
      BeepFlag = 0;//清除忙标志位
      BeepStatus = 0;//回到初始状态
    break;
  }
}

用这样的方法实现的蜂鸣程序在使用时也有不同的地方,因为使用的switch状态所有在主循环中要一直调用:

void main()
{
  SystemInitial();//系统初始化
  ...............

  //主循环
  while(1)
  {
     Fun1Proc();//功能1流程
     Fun2Proc();//功能2流程
     ....     
     BeepProc();//蜂鸣流程
     ....
  }

}

在别的函数中需要使蜂鸣器工作时只需要下面代码即可:

if(!BeepFlag)//检查是否忙
 BeepCtrl = 1;//启动蜂鸣器

用这种方法能充分利用MCU,在蜂鸣器发声或发声间隔的等待时间MCU可以处理别的函数,但是还要有几点需要注意

第一,主循环while(1)的循环周期最好小于定时器计数时钟周期
第二,主循环中尽量不要使用硬延时Delayms
第三,代码中如果存在多个地方需要控制一个流程时一定要先读取标志位再控制