找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 5462|回复: 13
收起左侧

基于单片机定时器,实现类似PLC多个软定时器的MCU编程思路(原创代码)

  [复制链接]
ID:471574 发表于 2020-11-13 15:09 | 显示全部楼层 |阅读模式
       单片机要想实现多个不同时基的控制,除了上系统外,本人采用分时段的方法,结合PLC的控制原理,实现出类似PLC定时器的控制方法,分别有0.01S定时器 12个,0.1MS定时器 10个,1S定时器10个,可以单独设定,同时使用。如果MCU的程序容量比较大,可以实现更多的定时器,不过一般建议根据实际项目要求来调整。实现过程思路代码如下(STC89单片机):
//以下程序及算法由本人zyhlove813原创,特别是soft_timer实现算法值得借鉴,适用所有MCU,源码示例请下载附件
#include "reg51.h"
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
/* define constants */
#define true 1
#define false 0
#define FOSC 11059200L    //时钟频率
#define T1MS (65536-FOSC/12/1000)   //1MS定时器设定值
#define T10ms 12    //0.01S定时器数组索引上限=12-1=11,因此0.01S定时器的个数=12个
#define T100ms (T10ms+10) //0.1S定时器数组索引上限=22-1=21,因此0.1S定时器的个数=21-11=10个
#define Timers 32   //1S定时器数组索引上限=32-1=31,因此1S定时器的个数=31-21=10个
#define Timers_bit (Timers/8)  //八个定时器为一组,共32/8=4组
#define SET_BIT(s,b,c)(s=(s&(~(1<<b)))+(c<<b))  //宏,设置某个位的值,0或1的状态
#define GET_BIT(s,b)((s>>b)&0x01)                //宏,获取某个位的值,返回0或1的状态
#define SET_EN(b,c) (SET_BIT(timer_en[b/8],b%8,c))  //设置对应定时器使能状态
#define SET_ON(b,c) (SET_BIT(timer_on[b/8],b%8,c))  //设置对应定时器对应使能状态
#define GET_EN(b) (GET_BIT(timer_en[b/8],b%8))     //获取对应定时器对应使能状态
#define GET_ON(b) (GET_BIT(timer_on[b/8],b%8))     //获取对应定时器线圈状态
uint8_t  timer_en[Timers_bit];    //定时器使能状态缓存
uint8_t  timer_on[Timers_bit];    //定时器线圈状态缓存
uint32_t  timer_pv[Timers];       //定时器目标值缓存
uint32_t  timer_cv[Timers];       //定时器当前值缓存
uint32_tcount_1ms;               //硬件定时器1MS计数器
sbit LED1=P1^1;                   //测试用的IO1
sbit LED2=P1^2;                     //测试用的IO2
void soft_timer(void);            //声明函数
void timer0_isr() interrupt 1    //定时器0中断,每1MS中断一次
{
   TL0 = T1MS;                     //reloadtimer0 low byte
   TH0 = T1MS>> 8;                //reload timer0 high byte
   soft_timer();                  //调用软件定时器判断
}
void main()
{
   TMOD = 0x01;                    //set timer0 as mode1 (16-bit)
   TL0 = T1MS;                     //initialtimer0 low byte
   TH0 = T1MS>> 8;                //initial timer0 high byte
   TR0 = 1;                        //timer0 start running
   ET0 = 1;                        //enable timer0 interrupt
   EA = 1;                         //open global interrupt switch
   count_1ms = 0;                  //initial counter
   timer_pv[1]=1;                    //0.01S定时器(1)的目标值设为1,即10MS
    timer_pv[2]=1;                  //0.01S定时器(2)的目标值设为1,即10MS
    timer_pv[12]=5;                 //0.1S定时器(12)的目标值设为5,即500MS
    timer_pv[13]=5;                 //0.1S定时器(13)的目标值设为5,即500MS
   SET_EN(1,1);                    //0.01S定时器(1)使能有效,开始计时
   SET_EN(12,1);                     //0.1S定时器(12)使能有效,开始计时
   while (1)
        {
             if(GET_ON(1))           //如果0.01S定时器(1)的定时线圈为1,即定时时间到
             {
                 LED1=1;             //LED1
                 SET_EN(2,1);        //0.01S定时器(2)使能有效,开始计时
                 SET_EN(1,0);        //0.01S定时器(1)使能无效,停止计时        
             }
             if(GET_ON(2))          //如果0.01S定时器(2)的定时线圈为1,即定时时间到
             {
                 LED1=0;            //LED1
                 SET_EN(1,1);       //0.01S定时器(1)使能有效,开始计时
                 SET_EN(2,0);       //0.01S定时器(2)使能无效,停止计时      
     }
             if(GET_ON(12))         //如果0.1S定时器(12)的定时线圈为1,即定时时间到
             {
                 LED2=1;             //LED2
                 SET_EN(13,1);        //0.1S定时器(13)使能有效,开始计时
                 SET_EN(12,0);        //0.1S定时器(12)使能无效,停止计时
             }
             if(GET_ON(13))
             {
                 LED2=0;              //LED2
                 SET_EN(12,1);        //0.1S定时器(12)使能有效,开始计时
                 SET_EN(13,0);        //0.1S定时器(13)使能无效,停止计时               
     }
   }
}
//软件定时器的实现
void soft_timer()
{
    uint8_ti;
    uint8_ttemp;
    count_1ms++;        //1MS计数值+1
    if(count_1ms%10==0)    //判断是否0.01S时间到
    {
        for(i=0;i<T10ms;i++)  //更新0.01S定时器的当前值
        {
             timer_cv+=GET_EN(i);  //如果EN=1,则当前值+1,否则+0
             timer_cv*=GET_EN(i);  //如果EN=1,则当前值不变,否则当前值=0 保障当前值根据使能状态自动加或清零
             temp=GET_EN(i)*(timer_cv>=timer_pv);   //计算是否到达目标时间,如果使能无效的话,结果是0,如果使能有效的话,而且当前值大于目标值,结果是1
             SET_ON(i,temp);  //更新线圈是否到时状态
        }
    }
    //以下算法相同
    if(count_1ms%100==0)   //判断是否0.1S时间到
    {
        for(i=T10ms;i<T100ms;i++)
        {
             timer_cv+=GET_EN(i);
             timer_cv*=GET_EN(i);
             temp=GET_EN(i)*(timer_cv>=timer_pv);
             SET_ON(i,temp);
        }   
    }
    if(count_1ms%1000==0)   //判断是否1S时间到
    {
        count_1ms=0;        //1MS计数器重启
        for(i=T100ms;i<Timers;i++)
        {
             timer_cv+=GET_EN(i);
             timer_cv*=GET_EN(i);
             temp=GET_EN(i)*(timer_cv>=timer_pv);
             SET_ON(i,temp);
        }
    }   
}   



全部资料51hei下载地址:
SoftTimer.rar (31.31 KB, 下载次数: 104)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:97678 发表于 2020-11-15 09:09 | 显示全部楼层
好东西!!!
下载来用用看!!!
回复

使用道具 举报

ID:240034 发表于 2021-1-15 14:41 | 显示全部楼层
定时部分程序结构不错,参考借用下,
回复

使用道具 举报

ID:878420 发表于 2021-3-3 15:33 来自手机 | 显示全部楼层
没有看懂啊
回复

使用道具 举报

ID:948578 发表于 2021-10-27 18:26 | 显示全部楼层
s,a,b,c分别是表示啥参数?
回复

使用道具 举报

ID:471574 发表于 2021-10-30 10:40 | 显示全部楼层
yjheeqgnui 发表于 2021-10-27 18:26
s,a,b,c分别是表示啥参数?

b表示第几个软定时器,c表示ON或为OFF,用于设置定时器EN或ON的状态
s表示软定时器变量,因为是数组(timer_en,timer_on),需要计算出索引值,
注意得是timer_en[0]中二进制每个位,对应了软件定时器0-7的状态
回复

使用道具 举报

ID:875095 发表于 2021-10-31 10:21 | 显示全部楼层
嗯,这几天也在想定时问题,借鉴一下
回复

使用道具 举报

ID:346397 发表于 2022-5-30 18:50 | 显示全部楼层
思路不错
回复

使用道具 举报

ID:105206 发表于 2022-7-3 07:49 | 显示全部楼层
漂亮,感谢楼主分享。
回复

使用道具 举报

ID:462629 发表于 2022-7-9 15:25 | 显示全部楼层
这个思路可以,学习了
回复

使用道具 举报

ID:914666 发表于 2022-7-11 12:50 | 显示全部楼层
51hei有你更精彩
回复

使用道具 举报

ID:71535 发表于 2022-7-27 20:27 | 显示全部楼层
算法思路值得学习,学习分享
回复

使用道具 举报

ID:137736 发表于 2022-10-31 16:44 | 显示全部楼层
顶,软定时器,本人正在学习,编了一个仿Arduno的millis程序,实现多程序非阻塞运行
回复

使用道具 举报

ID:334781 发表于 2023-1-31 10:35 | 显示全部楼层
下载过来,研究一下,这个定时的思路不错!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表