小型的代码框架解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baiyibin0530/article/details/83780926

架构的运行机制

 

 

(首先给大家看个例子,如果这个例子大家能够看得懂,那么后面的框架大家也一样可以看懂。)

static int8_t CUSTOM_HID_OutEvent_FS(uint8_t* event_idx)

{

       Flag1 = 1;

}

 

while(1)

{

If(1 == flag1 || 1==flag2)

{

       flag1 = 0;

       Task1;

}

}

(上面的程序标志位有两个一个是flag1另一个是flag2。下面的框架是多个任务,也就是多个标志位。每个标志位对应着一个任务。在调度的过程中有两类标志位,一类标志位时可变的,下面你会看到这类标志位有三个动作,置标志位->判断标志位->清楚标志位。然而另一类标志位只有两个动作,置标志位->判断标志位。

while (1)

{

task_handle();

}

 

void task_handle(void)

{

    uint8_t i;      

    for (i = 0; i < ARRAY_SIZE(tasks); ++i)

    {

        if ((is_task_set(tasks[i].id))

            || (is_task_always_alive(tasks[i].flags)))

        {

            reset_task(tasks[i].id);

            tasks[i].handle(tasks[i].args);

        }

    }

}

从代码上来看这是一个轮询机制。

 

调度函数的条件有两个如下:

  1. is_task_set(tasks[i].id)  相当于小程序的(if(flag1 == )
  2. is_task_always_alive(tasks[i].flags)((if(flag2 == 1)))

 

那么这个两个标志位的不同在哪里?

下面会详细讲解

 

 

第一种调度的原理:

标志位被置位的条件:

  • 中断置位,在应用程序中调度任务函数(USB下发的测试,升级等任务)
  • 定时置位,使应用函数在固定的时间间隔内被调度执行(读取touchpad,按键等任务)
  • 在任务中调度另一个任务

我们先看第一种条件的调度:

  • notify(tasks[i].id);  相当于 flag=1
  • is_task_set(tasks[i].id)  相当于 if(1==flag)
  • reset_task(tasks[i].id)  相当于 flag=0

 

 

 

struct task

{

    uint8_t id;

    uint8_t flags;

    void *args;

    void (*handle)(void *args);

};

 

对应于下列:

const struct task tasks[] =

{        uint8_t id;          uint8_t flags;        void *args;     void (*handle)(void *args)

    {EV_TICK_LITTLE,     ALWAYS_ALIVE,     NULL,     sys_tick_little},  

    {EV_READ,            ALWAYS_ALIVE,     NULL,     read_rf_status_cmd},

    {EV_CONTROL_IR,           0,             NULL,     period_control_ir},

    {EV_100MS,                  0,             NULL,     task_100ms},//

    {EV_SEC,                    0,             NULL,     task_sec},

    {EV_TICK,             ALWAYS_ALIVE,     NULL,     sys_tick},

    {EV_REPORT_DATA,          0,             NULL,     report_data_rf},        

    {EV_USB_TASK,              0,             NULL,    handle_usb_task},

    {EV_3MS,                    0,             NULL,    task_3ms},

    {EV_6MS,                    0,             NULL,    task_6ms},

    {EV_12MS,                   0,             NULL,    task_12ms},

    {EV_CALIBRATE_GRY_ACC,   0,             NULL,    calibrate_gry_acc},

              

}; 

 

解读代码:

ARRAY_SIZE(tasks)

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

计算出任务的个数。

第一种条件下的调度原理,先看这个函数的展开:

  • notify(tasks[i].id);

#define notify(task_bit)         set_bit(task_monitor, task_bit)

#define set_bit(x, bit)        do{ __disable_irq();(x) |= 1 << (bit);__enable_irq();}while(0)

注:执行置位的时候开启了总中断,其实中断和应用程序在用同一个资源,要保证同一资源在一个时候是唯一被应用的。

  • is_task_set(tasks[i].id)

#define is_task_set(task_bit)  is_bit_set(task_monitor, task_bit)

#define is_bit_set(x, bit)       ((x) & (1 << (bit)))

 

  • reset_task(tasks[i].id)

#define reset_task(task_bit)   reset_bit(task_monitor, task_bit)

#define reset_bit(x, bit)        do{ __disable_irq();(x) &= ~(1 << (bit));__enable_irq();}while(0)

 

  • is_task_always_alive(tasks[i].flags))

#define is_task_always_alive(flags)      (flags & ALWAYS_ALIVE)

enum

{

ALWAYS_ALIVE = 0x01

};

 

 

上面这三个函数里,有一个共同的全局变量task_monitor,在代码里的定义为:

uint16_t task_monitor;

三个函数项目间的联系是通过这个全局变量task_monitor,从定义上看,这是一个16位的全局变量,也就意味着我们的框架最多可以调度16个任务,每一个任务占用一位。

16个任务对于一般的项目是够用了,如果不够用这个框架还可以扩展到32个任务,可以修改如下:

uint32_t  task_monitor;

 

下面是定义的ID号:

enum

{

    EV_TICK_LITTLE,

    EV_READ,

    EV_CONTROL_IR,

    EV_100MS,

    EV_SEC,

    EV_TICK,

    EV_USB_TASK,

    EV_3MS,

    EV_6MS,

    EV_12MS,

    EV_CALIBRATE_GRY_ACC,

};

 

目前用到的是14个任务,从左往右,从上往下,是对应16位task_monitor;的从低位到高位,如下图对应:

EV_TICK_LITTLE

EV_READ

EV_CONTROL_IR

EV_100MS

EV_SEC

EV_TICK

EV_MIN

EV_CALLBACK

EV_USB_TASK

EV_3MS

EV_6MS

EV_12MS

EV_CALIBRATE_GRY_ACC

EV_REPORT_DATA

 

 

 

第一种情况下函数调度的调度原理如下:

 

task_monitor

is_task_set

USB等中断

reset_task

notify

调度对应的函数

定时器

 

 

 

 

 

第二种调度的原理以及定时任务的建立:

 

is_task_always_alive(tasks[i].flags

如果任务对应的flag 是ALWAYS_ALIVE,那么轮询到这个flag就会调用对应的函数。

const struct task tasks[] =

{        uint8_t id;          uint8_t flags;        void *args;     void (*handle)(void *args)

    {EV_TICK_LITTLE,     ALWAYS_ALIVE,     NULL,     sys_tick_little},  

    {EV_READ,            ALWAYS_ALIVE,     NULL,     read_rf_status_cmd},

    {EV_CONTROL_IR,           0,             NULL,     period_control_ir},

    {EV_100MS,                  0,             NULL,     task_100ms},//

    {EV_SEC,                    0,             NULL,     task_sec},

    {EV_TICK,             ALWAYS_ALIVE,     NULL,     sys_tick},

    {EV_REPORT_DATA,          0,             NULL,     report_data_rf},        

    {EV_USB_TASK,              0,             NULL,    handle_usb_task},

    {EV_3MS,                    0,             NULL,    task_3ms},

    {EV_6MS,                    0,             NULL,    task_6ms},

    {EV_12MS,                   0,             NULL,    task_12ms},

    {EV_CALIBRATE_GRY_ACC,   0,             NULL,    calibrate_gry_acc},

              

}; 

从上面的任务看使用ALWAYS_ALIVE 的任务有三个分别是sys_tick_little,read_rf_status_cmd,sys_tick 。

sys_tick_little和sys_tick是用来驱动定时任务的。

 

sys_tick函数驱动的任务:

 

 

 

 

 

 

 

 

sys_tick_little

notify(EV_12MS)

 

notify(EV_3MS)

notify(EV_6MS)

 

sys_tick

notify(EV_MIN)

 

notify(EV_100MS)

notify(EV_SEC)

 

定时调度任务是用time2来进行计时的。

 

 

notify(EV_100MS)

TIME2

_sys_tick

last_tick

sys_tick

notify(EV_MIN)

 

notify(EV_SEC)

 

 

可以讲解一个例子来说明

 

每一个事务的出现都有它的两面性,比如核能的出现,可以用来发电也可以用来制造武器。现在MCU(也就是单片机)常用的操作系统是ucoss系列和freertos,企业最常用的是freertos。用系统比框架有很多的优势,这里就不多说了。如果我们的产品功能不是太多就没有必要用系统,再者就是系统会占用flash空间。这个框架在一个小时以内就可以入门,系统需要的时间较长一点。系统还会有移植的问题,但是现在不比担心了,如果你想用freertos,用ST官方提供的Cube就可以自动生成。Cube是一个很好用的工具,可以有空学习一下。

 

这个框架的缺点:

 

  1. 里面的任务没有优先级。
  2. 任务最好碎片化处理,每个任务的执行时间最好不好超过你最小的定时任务。(假设你的最小定时任务是3ms,然而有一个任务是4ms的执行时间,那么你的定时任务最小应该是4ms,而不是3ms)。
  3. 假设最小的定时间隔是3ms,其他任务的执行时间也小于3ms,那么定时3ms的任务会出现不准确的现象,你的定时任务会出现:

3ms <= 定时任务时间 <= 3ms  +  (除3ms以外任务运行的时间)

 

 

read_rf_status_cmd

handle_usb_task

report_data_rf

task_3ms

sys_tick_little

 

 

 

 

 

 

 

 

作者:albert

猜你喜欢

转载自blog.csdn.net/baiyibin0530/article/details/83780926