打印
[经验分享]

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

[复制链接]
2089|54
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
   我学单片机初期的时候是没有接触过什么操作系统的,后续接触过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

使用特权

评论回复
沙发
iyoum| | 2024-12-9 14:07 | 只看该作者

单片机裸机常用的时间片轮训系统是一种简单的多任务调度机制,它允许在没有操作系统的情况下模拟并发执行多个任务。

使用特权

评论回复
板凳
saservice| | 2024-12-10 15:26 | 只看该作者
时间片轮训系统也有其缺点,比如它不支持任务的优先级调度,所有任务都按照固定的时间片执行,这可能导致某些紧急任务无法及时处理。此外,如果任务数量较多,或者任务的时间片设置不合理,可能会导致系统效率低下。

使用特权

评论回复
地板
jimmhu| | 2024-12-10 16:56 | 只看该作者
时间片长度和任务划分需要精心设计。如果时间片长度设置不合理,可能会导致某些任务无法在规定时间内完成,或者 CPU 资源浪费在频繁的任务切换上。

使用特权

评论回复
5
febgxu| | 2024-12-10 18:46 | 只看该作者
实现简单,不需要操作系统支持。
可以在资源受限的嵌入式系统中使用。

使用特权

评论回复
6
albertaabbot| | 2024-12-10 19:44 | 只看该作者
虽然传统的时间片轮训是非抢占式的,但在某些情况下你可能希望引入优先级概念,允许高优先级的任务打断低优先级的任务。这可以通过修改调度算法来实现,例如总是先检查是否有更高优先级的任务正在等待执行。

使用特权

评论回复
7
deliahouse887| | 2024-12-10 20:22 | 只看该作者
由于采用轮询方式,某些任务的响应时间可能会受到其他任务的影响。

使用特权

评论回复
8
cemaj| | 2024-12-11 17:26 | 只看该作者
定义一个任务结构体,包括任务主体函数、分配给任务的时间片tick数和当前运行已经消耗的时间片tick计数。

使用特权

评论回复
9
gygp| | 2024-12-12 12:33 | 只看该作者
使用定时器中断来触发任务的执行。每当定时器中断发生时,调度器就会检查哪些任务的时间片已到,并执行相应的任务。

使用特权

评论回复
10
robincotton| | 2024-12-12 16:30 | 只看该作者
在主循环中,不断轮询所有任务的状态。如果某个任务处于就绪状态,则执行该任务的任务函数。
执行完任务后,根据需要重新装载该任务的时间片,以便在下一个周期再次执行。

使用特权

评论回复
11
iyoum| | 2024-12-12 19:26 | 只看该作者
时间片轮询系统是一种在单片机裸机环境下实现多任务处理的简单有效方法。它的基本思想是将 CPU 的运行时间划分为多个时间片,每个任务按照预先分配的时间片轮流执行。当一个任务在其分配的时间片内没有完成,它会暂停执行,等待下一个时间片到来再继续执行,就像多个任务在排队轮流使用 CPU 资源一样。

使用特权

评论回复
12
pl202| | 2024-12-13 20:36 | 只看该作者
编写一个简单的调度器函数,负责决定哪个任务应该被执行。可以是一个循环,依次检查每个任务是否准备好运行。

使用特权

评论回复
13
louliana| | 2024-12-13 21:01 | 只看该作者
使用一个结构体数组来管理所有任务,每个结构体包含了任务的状态(如就绪、挂起等)、时间片、任务函数指针等信息。

使用特权

评论回复
14
everyrobin| | 2024-12-14 12:53 | 只看该作者
配置一个定时器产生周期性的中断,在每次中断时调用调度器进行上下文切换。

使用特权

评论回复
15
iyoum| | 2024-12-14 13:33 | 只看该作者
在单片机裸机编程中,时间片轮训是一种常用的多任务调度方法,它允许系统在多个任务之间分配CPU时间,从而提高系统的响应性和效率。

使用特权

评论回复
16
mmbs| | 2024-12-14 14:56 | 只看该作者
采用动态时间片分配方法。根据任务的实际执行情况和优先级,动态地调整每个任务的时间片长度,提高系统的灵活性和适应性。

使用特权

评论回复
17
eefas| | 2024-12-14 15:48 | 只看该作者
通过合理分配时间片和有效管理任务,可以在没有操作系统的情况下实现多任务的模拟并发执行。

使用特权

评论回复
18
chenci2013| | 2024-12-14 16:33 | 只看该作者

在单片机裸机编程中,时间片轮询系统是一种常用的多任务处理方法。

使用特权

评论回复
19
lzbf| | 2024-12-14 17:06 | 只看该作者
任务响应及时性相对较差。如果一个任务需要紧急处理,但它还没有轮到自己的时间片,就需要等待,这可能会导致任务响应延迟,在对实时性要求极高的系统中可能不适用。

使用特权

评论回复
20
sesefadou| | 2024-12-15 10:37 | 只看该作者
通过这种方式,单片机可以在没有操作系统的情况下实现简单的多任务处理。

使用特权

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

本版积分规则

77

主题

4156

帖子

5

粉丝