打印
[经验分享]

单片机裸机常用的时间片轮训系统

[复制链接]
109|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
   我学单片机初期的时候是没有接触过什么操作系统的,后续接触过FreeRTOS,了解过一下时间片轮训和抢占式。但是工作发现许多工控行业的产品用不到移植操作系统。

        那我学习初期遇到一种问题就很棘手,比如我想在ADC采集温度到88度时,打开一个继电器,延时30后,打开第二个继电器。这个延时肯定不能在while{1}里面用delay延时,会导致整个程序都卡住30s。为了处理这种问题可以采取下面这种写法。

        time.h头文件代码:

#include <stdint.h>

#ifndef _TIME_H
#define _TIME_H



typedef enum{
        Task_1MS,
        Task_10MS,
        Task_50MS,
        Task_100MS,
        Task_500MS,
        Task_1000MS,
        Task_Max
}Sys_Task_Type;

void Tim6_Init(void);
void Timer_CallBack_Handler(void);
void Sys_Task_Reload(Sys_Task_Type type);
extern uint16_t Sys_Run_Task[Task_Max];

#endif

time.c源文件代码:

#include "time.h"

uint16_t Sys_Run_Task[Task_Max] = {1,10,50,100,500,1000};


//基本定时器
void Tim6_Init()
{
        timer_parameter_struct timer_initpara;
    rcu_timer_clock_prescaler_config (RCU_TIMER_PSC_MUL4);
    timer_deinit(TIMER6);

    timer_initpara.prescaler         = 168-1;                                        //分频系数
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 1000-1;                                //重装载值
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER6,&timer_initpara);

        timer_update_event_enable(TIMER6);                                                //更新事件
        timer_interrupt_flag_clear(TIMER6,TIMER_FLAG_UP);                //清除更新中断标志位
    timer_interrupt_enable(TIMER6,TIMER_INT_FLAG_UP);                //更新中断使能

        nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
        nvic_irq_enable(TIMER6_IRQn,2,2);
       
        timer_enable(TIMER6);                                                                        //启用计数器
}


void TIMER6_IRQHandler(void)
{
        if(RESET != timer_interrupt_flag_get(TIMER6,TIMER_INT_FLAG_UP))
        {
                Timer_CallBack_Handler();       
                timer_interrupt_flag_clear(TIMER6,TIMER_INT_FLAG_UP);//清除更新中断标志位
        }
}


//遍历 Sys_Run_Task 数组,将每个任务的剩余运行时间减一
void Timer_CallBack_Handler(void)
{
        uint8_t index = 0;
        for(index = 0;index < Task_Max;index++)
        {
                if(Sys_Run_Task[index] > 0)
                {
                        Sys_Run_Task[index]--;
                }
        }
}


void Sys_Task_Reload(Sys_Task_Type type)//定义一个枚举类型的变量
{
        switch((uint8_t)type)
        {
                case Task_1MS://0
                {
                        Sys_Run_Task[type] = 1;
                        break;
                }
                case Task_10MS:
                {
                        Sys_Run_Task[type] = 10;
                        break;
                }
                case Task_50MS:
                {
                        Sys_Run_Task[type] = 50;
                        break;
                }
                case Task_100MS:
                {
                        Sys_Run_Task[type] = 100;
                        break;
                }
                case Task_500MS:
                {
                        Sys_Run_Task[type] = 500;
                        break;
                }
                case Task_1000MS:
                {
                        Sys_Run_Task[type] = 1000;
                        break;
                }
        }
}

上面有4个函数

一个是配置1ms进一次中断的基本定时器初始化函数;

一个中断服务函数,用来调用自减函数;

一个循环遍历的自减函数;

一个填充赋值函数;

main.c文件

include "main.h"


int main(void)
{
        All_Periph_Clock_Enable();                                                        //外设时钟使能
        All_Config();                                                                                //外设初始化
        Nvic_Config();                                                                                //中断通道配置       

    while(1)
        {       
                if(Sys_Run_Task[Task_1MS] == 0)
                {
                       
                        Sys_Task_Reload(Task_1MS);
                }
                if(Sys_Run_Task[Task_10MS] == 0)
                {

                        Sys_Task_Reload(Task_10MS);
                }
                if(Sys_Run_Task[Task_50MS] == 0)
                {
                       
                        Sys_Task_Reload(Task_50MS);
                }
                if(Sys_Run_Task[Task_100MS] == 0)
                {
                       
                       
                        Sys_Task_Reload(Task_100MS);
                }
                if(Sys_Run_Task[Task_500MS] == 0)
                {
                       
                        Sys_Task_Reload(Task_500MS);
                }
                if(Sys_Run_Task[Task_1000MS] == 0)
                {
                       

                        Sys_Task_Reload(Task_1000MS);
                }
        }
}

        基本逻辑就是每ms执行一次中断,将数组的数据都减1,在main.c里面的while{1}里,循环的判断数组的数据什么时候减到为0,等于0的立马给填充回原先设定的时间。上面设定了1,10,50,100,500,1000ms不同的时间,时间一到就执行对应部分。

        现在再想处理最初的30s延时功能,只需要在1s的条件下,设定一个标志累加30次,到30的时候清除,去打开继电器就好了,当然这里提到的这种处理方式在项目中肯定有弊端,延时需求多标志位设立太多,不过后面我还会整理其他笔记。上面就是很常用的时间片轮训的一种简单架构写法。其他写法的原理也都差不多,都是通过定时器计时。有更高深,或者其他方式希望指导。



————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/GQli2296523792/article/details/144116686

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

72

主题

4141

帖子

5

粉丝