STM32任务调度 - 操作系统/RT-Thread

详见RT-Thread API参考手册:模块->内核;

RT-Thread 内核,是 RT-Thread 的核心部分,包括了内核系统中对象的实现, 例如多线程及其调度、信号量、邮箱、消息队列、内存管理、定时器等;

libcpu/BSP(芯片移植相关文件 / 板级支持包)与硬件密切相关,由外设驱动和 CPU 移植构成。

一、操作系统概述

1、实时操作系统

实时操作系统(RTOS)指能在确定的时间内执行其功能并对外部的异步事件做出响应的计算机系统;

是以优先级为基本调度原则,高优先级的任务可以抢占低优先级任务的CPU使用权,其他同步机制辅助;

主要特点是:系统响应比较及时(快),常用的实时操作系统有:UCosII、FreeROTS、RT-Thread等。

2、分时操作系统  

分时操作系统(TSOS)是以时间片轮询为基本调度原则,给当前所有任务分配一定分数的时间配额(例如:1S分成1000份,每份1ms,给A任务分100份,给B分200….);

主要特点是:有效增强系统资源的利用率,常用的分时操作系统有:Linux2.6之前版本、windowCE版本等。

3、半实时操作系统

半实时操作系统具有实时操作系统特征也有分时操作系统特征,常用的半实时操作系统有:Linux2.6之后版本、windowCE之后版本。

二、RT-Thread定时器管理

RT-Thread的定时器提供两类定时器机制:第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件, 然后定时器自动停止,第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止,否则将永远持续执行下去。

/*

* 程序清单:定时器例程

*

* 这个例程会创建两个动态定时器,一个是单次定时,一个是周期性定时

* 并让周期定时器运行一段时间后停止运行

*/

#include <rtthread.h>

#define DBG_TAG "main"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

/* 定时器的控制块 */

static rt_timer_t timer1;

static rt_timer_t timer2;

static int cnt = 0;

/* 定时器1超时函数 */

static void timeout1(void *parameter)

{

    rt_kprintf("periodic timer is timeout %d\n", cnt);

    /* 运行第10次,停止周期定时器 */

    if (cnt++ >= 9) {

        rt_timer_stop(timer1);

        rt_kprintf("periodic timer was stopped! \n");

    }

}

/* 定时器2超时函数 */

static void timeout2(void *parameter)

{

    rt_kprintf("one shot timer is timeout\n");

}

int timer_sample(void)

{

    /* 创建定时器1  周期定时器 */

    timer1 = rt_timer_create("timer1", timeout1,

                             RT_NULL, 10,

                             RT_TIMER_FLAG_PERIODIC);

    /* 启动定时器1 */

    if (timer1 != RT_NULL)

        rt_timer_start(timer1);

    /* 创建定时器2 单次定时器 */

    timer2 = rt_timer_create("timer2", timeout2,

                             RT_NULL,  30,

                             RT_TIMER_FLAG_ONE_SHOT);

    /* 启动定时器2 */

    if (timer2 != RT_NULL)

        rt_timer_start(timer2);

    return 0;

}

int main(void)

{

    timer_sample();

    while (1) {

        rt_thread_mdelay(1000);

    }

    return RT_EOK;

}

三、RT-Thread线程管理

1、时钟管理

// 获取当前时间节拍

rt_tick_t rt_tick_get(void)

2、线程创建/初始化/脱离

/*

* 程序清单:创建、初始化/脱离线程

*

* 这个例子会创建两个线程,一个动态线程,一个静态线程

* 静态线程在运行完毕后自动被系统脱离,动态线程一直打印计数

*/

#include <rtthread.h>

#define DBG_TAG "main"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

#define THREAD_PRIORITY         25

#define THREAD_STACK_SIZE       512

#define THREAD_TIMESLICE        5

static rt_thread_t tid1 = RT_NULL;

/* 线程1的入口函数 */

static void thread1_entry(void *parameter)

{

    rt_uint32_t count = 0;

    while (1) {

        /* 线程1采用低优先级运行,一直打印计数值 */

        rt_kprintf("thread1 count: %d\n", count ++);

        rt_thread_mdelay(500);

    }

}

ALIGN(RT_ALIGN_SIZE)

static char thread2_stack[1024];

static struct rt_thread thread2;

/* 线程2入口 */

static void thread2_entry(void *param)

{

    rt_uint32_t count = 0;

    /* 线程2拥有较高的优先级,以抢占线程1而获得执行 */

    for (count = 0; count < 10 ; count++) {

        /* 线程2打印计数值 */

        rt_kprintf("thread2 count: %d\n", count);

    }

    rt_kprintf("thread2 exit\n");

    /* 线程2运行结束后也将自动被系统脱离 */

}

/* 线程示例 */

int thread_sample(void)

{

    /* 创建线程1,名称是thread1,入口是thread1_entry*/

    tid1 = rt_thread_create("thread1",

                            thread1_entry, RT_NULL,

                            THREAD_STACK_SIZE,

                            THREAD_PRIORITY, THREAD_TIMESLICE);

    /* 如果获得线程控制块,启动这个线程 */

    if (tid1 != RT_NULL)

        rt_thread_startup(tid1);

    /* 初始化线程2,名称是thread2,入口是thread2_entry */

    rt_thread_init(&thread2,

                   "thread2",

                   thread2_entry,

                   RT_NULL,

                   &thread2_stack[0],

                   sizeof(thread2_stack),

                   THREAD_PRIORITY - 1, THREAD_TIMESLICE);

    rt_thread_startup(&thread2);

    return 0;

}

int main(void)

{

thread_sample();

    while (1) {

        rt_thread_mdelay(1000);

    }

    return RT_EOK;

}

3、时间片调度

/*

* 程序清单:相同优先级线程按照时间片轮转调度

*

* 这个例子中将创建两个线程,每一个线程都在打印信息

*

*/

#include <rtthread.h>

#define DBG_TAG "main"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

#define THREAD_STACK_SIZE   1024

#define THREAD_PRIORITY     20

#define THREAD_TIMESLICE    10 // 决定线程调度时每次可以运行的最大时间节拍,0为系统默认值决定

/* 线程入口 */

static void thread_entry(void *parameter)

{

    rt_uint32_t value;

    rt_uint32_t count = 0;

    value = (rt_uint32_t)parameter;

    while (1) {

        if (0 == (count % 5)) {

            rt_kprintf("thread %d is running ,thread %d count = %d\n", value, value, count);

            if (count > 200)

                return;

        }

        count++;

        rt_thread_mdelay(500);

    }

}

int timeslice_sample(void)

{

    rt_thread_t tid = RT_NULL;

    /* 创建线程1 */

    tid = rt_thread_create("thread1",

                           thread_entry, (void *)1,

                           THREAD_STACK_SIZE,

                           THREAD_PRIORITY, THREAD_TIMESLICE);

    if (tid != RT_NULL)

        rt_thread_startup(tid);

    /* 创建线程2 */

    tid = rt_thread_create("thread2",

                           thread_entry, (void *)2,

                           THREAD_STACK_SIZE,

                           THREAD_PRIORITY, THREAD_TIMESLICE - 5);

    if (tid != RT_NULL)

        rt_thread_startup(tid);

    return 0;

}

int main(void)

{

    timeslice_sample();

    while (1) {

        rt_thread_mdelay(1000);

    }

    return RT_EOK;

}

4、调度器钩子函数

/*

* 程序清单:调度器钩子

* 在调度器钩子中打印线程切换信息

*/

#include <rtthread.h>

#define DBG_TAG "main"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

#define THREAD_STACK_SIZE   1024

#define THREAD_PRIORITY     20

#define THREAD_TIMESLICE    10

/* 针对每个线程的计数器 */

volatile rt_uint32_t count[2];

/* 线程1、2共用一个入口,但入口参数不同 */

static void thread_entry(void *parameter)

{

    rt_uint32_t value;

    value = (rt_uint32_t)parameter;

    while (1)

    {

        rt_kprintf("thread %d is running\n", value);

        rt_thread_mdelay(1000); //延时一段时间

    }

}

static rt_thread_t tid1 = RT_NULL;

static rt_thread_t tid2 = RT_NULL;

static void hook_of_scheduler(struct rt_thread *from, struct rt_thread *to)

{

    rt_kprintf("from: %s -->  to: %s \n", from->name, to->name);

}

int scheduler_hook(void)

{

    /* 设置调度器钩子 */

    rt_scheduler_sethook(hook_of_scheduler);

    /* 创建线程1 */

    tid1 = rt_thread_create("thread1",

                            thread_entry, (void *)1,

                            THREAD_STACK_SIZE,

                            THREAD_PRIORITY, THREAD_TIMESLICE);

    if (tid1 != RT_NULL)

        rt_thread_startup(tid1);

    /* 创建线程2 */

    tid2 = rt_thread_create("thread2",

                            thread_entry, (void *)2,

                            THREAD_STACK_SIZE,

                            THREAD_PRIORITY, THREAD_TIMESLICE - 5);

    if (tid2 != RT_NULL)

        rt_thread_startup(tid2);

    return 0;

}

int main(void)

{

    scheduler_hook();

    while (1) {

        rt_thread_mdelay(1000);

    }

    return RT_EOK;

}

5、空闲线程钩子函数

/*

* 程序清单:空闲任务钩子例程

*

* 这个例程创建一个线程,通过延时进入空闲任务钩子,用于打印进入空闲钩子的次数

*/

#include <rtthread.h>

#include <rthw.h>

#define DBG_TAG "main"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

#define THREAD_PRIORITY      20

#define THREAD_STACK_SIZE    2048

#define THREAD_TIMESLICE     5

/* 指向线程控制块的指针 */

static rt_thread_t tid = RT_NULL;

/* 空闲函数钩子函数执行次数 */

volatile static int hook_times = 0;

/* 空闲任务钩子函数 */

static void idle_hook()

{

    if (0 == (hook_times % 10000)) {

        rt_kprintf("enter idle hook %d times.\n", hook_times);

    }

    rt_enter_critical();

    hook_times++;

    rt_exit_critical();

}

/* 线程入口 */

static void thread_entry(void *parameter)

{

    int i = 5;

    while (i--) {

        rt_kprintf("enter thread1.\n");

        rt_enter_critical();

        hook_times = 0;

        rt_exit_critical();

        /* 休眠500ms */

        rt_kprintf("thread1 delay 50 OS Tick.\n", hook_times);

        rt_thread_mdelay(500);

    }

    rt_kprintf("delete idle hook.\n");

    /* 删除空闲钩子函数 */

    rt_thread_idle_delhook(idle_hook);

    rt_kprintf("thread1 finish.\n");

}

int idle_hook_sample(void)

{

    /* 设置空闲线程钩子 */

    rt_thread_idle_sethook(idle_hook);

    /* 创建线程 */

    tid = rt_thread_create("thread1",

                           thread_entry, RT_NULL,

                           THREAD_STACK_SIZE,

                           THREAD_PRIORITY, THREAD_TIMESLICE);

    if (tid != RT_NULL)

        rt_thread_startup(tid);

    return 0;

}

int main(void)

{

    idle_hook_sample();

    while (1) {

        rt_thread_mdelay(1000);

    }

    return RT_EOK;

}

四、RT-Thread线程间通信

1、事件

一个事件集可以包含多个事件,利用事件集可以完成一对多,多对多的线程间同步。

/*

* 程序清单:事件例程

*

* 程序会初始化2个线程及初始化一个静态事件对象

* 一个线程等待于事件对象上,以接收事件;

* 一个线程发送事件 (事件3/事件5)

*/

#include <rtthread.h>

#define DBG_TAG "main"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

#define THREAD_PRIORITY      9

#define THREAD_TIMESLICE     5

#define EVENT_FLAG3 (1 << 3)

#define EVENT_FLAG5 (1 << 5)

/* 事件控制块 */

static struct rt_event event;

ALIGN(RT_ALIGN_SIZE)

static char thread1_stack[1024];

static struct rt_thread thread1;

/* 线程1入口函数 */

static void thread1_recv_event(void *param)

{

    rt_uint32_t e;

    /* 第一次接收事件,事件3或事件5任意一个可以触发线程1,接收完后清除事件标志 */

    if (rt_event_recv(&event, (EVENT_FLAG3 | EVENT_FLAG5),

                      RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,

                      RT_WAITING_FOREVER, &e) == RT_EOK) {

        rt_kprintf("thread1: OR recv event 0x%x\n", e);

    }

    rt_kprintf("thread1: delay 1s to prepare the second event\n");

    rt_thread_mdelay(1000);

    /* 第二次接收事件,事件3和事件5均发生时才可以触发线程1,接收完后清除事件标志 */

    if (rt_event_recv(&event, (EVENT_FLAG3 | EVENT_FLAG5),

                      RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,

                      RT_WAITING_FOREVER, &e) == RT_EOK) {

        rt_kprintf("thread1: AND recv event 0x%x\n", e);

    }

    rt_kprintf("thread1 leave.\n");

}

ALIGN(RT_ALIGN_SIZE)

static char thread2_stack[1024];

static struct rt_thread thread2;

/* 线程2入口 */

static void thread2_send_event(void *param)

{

    rt_kprintf("thread2: send event3\n");

    rt_event_send(&event, EVENT_FLAG3);

    rt_thread_mdelay(200);

    rt_kprintf("thread2: send event5\n");

    rt_event_send(&event, EVENT_FLAG5);

    rt_thread_mdelay(200);

    rt_kprintf("thread2: send event3\n");

    rt_event_send(&event, EVENT_FLAG3);

    rt_kprintf("thread2 leave.\n");

}

int event_sample(void)

{

    rt_err_t result;

    /* 初始化事件对象 */

    result = rt_event_init(&event, "event", RT_IPC_FLAG_FIFO);

    if (result != RT_EOK) {

        rt_kprintf("init event failed.\n");

        return -1;

    }

    rt_thread_init(&thread1,

                   "thread1",

                   thread1_recv_event,

                   RT_NULL,

                   &thread1_stack[0],

                   sizeof(thread1_stack),

                   THREAD_PRIORITY - 1, THREAD_TIMESLICE);

    rt_thread_startup(&thread1);

    rt_thread_init(&thread2,

                   "thread2",

                   thread2_send_event,

                   RT_NULL,

                   &thread2_stack[0],

                   sizeof(thread2_stack),

                   THREAD_PRIORITY, THREAD_TIMESLICE);

    rt_thread_startup(&thread2);

    return 0;

}

int main(void)

{

    event_sample();

    while (1) {

        rt_thread_mdelay(1000);

    }

    return RT_EOK;

}

2、互斥量

互斥量又叫相互排斥的信号量,是一种特殊的二值信号量,互斥量支持递归访问且能防止线程优先级翻转。

/*

* 程序清单:互斥锁例程

*

* 互斥锁是一种保护共享资源的方法。当一个线程拥有互斥锁的时候,

* 可以保护共享资源不被其他线程破坏。线程1对2个number分别进行加1操作

* 线程2也会对2个number分别进行加1操作。使用互斥量保证2个number值保持一致

*/

#include <rtthread.h>

#define DBG_TAG "main"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

#define THREAD_PRIORITY         8

#define THREAD_TIMESLICE        5

/* 指向互斥量的指针 */

static rt_mutex_t dynamic_mutex = RT_NULL;

static rt_uint8_t number1, number2 = 0;

ALIGN(RT_ALIGN_SIZE)

static char thread1_stack[1024];

static struct rt_thread thread1;

static void rt_thread_entry1(void *parameter)

{

    while (1) {

        /* 线程1获取到互斥量后,先后对number1、number2进行加1操作,然后释放互斥量 */

        rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);

        number1++;

        rt_thread_mdelay(10);

        number2++;

        rt_mutex_release(dynamic_mutex);

    }

}

ALIGN(RT_ALIGN_SIZE)

static char thread2_stack[1024];

static struct rt_thread thread2;

static void rt_thread_entry2(void *parameter)

{

    while (1) {

        /* 线程2获取到互斥量后检查number1、number2值是否相同,相同则表示mutex起到锁的作用 */

        rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);

        if (number1 != number2) {

            rt_kprintf("not protect.number1 = %d, mumber2 = %d \n", number1, number2);

        } else {

            rt_kprintf("mutex protect ,number1 = mumber2 is %d\n", number1);

        }

        number1++;

        number2++;

        rt_mutex_release(dynamic_mutex);

        if (number1 >= 50)

            return;

    }

}

/* 互斥量示例的初始化 */

int mutex_sample(void)

{

    /* 创建一个动态互斥量 */

    dynamic_mutex = rt_mutex_create("dmutex", RT_IPC_FLAG_FIFO);

    if (dynamic_mutex == RT_NULL) {

        rt_kprintf("create dynamic mutex failed.\n");

        return -1;

    }

    rt_thread_init(&thread1,

                   "thread1",

                   rt_thread_entry1,

                   RT_NULL,

                   &thread1_stack[0],

                   sizeof(thread1_stack),

                   THREAD_PRIORITY, THREAD_TIMESLICE);

    rt_thread_startup(&thread1);

    rt_thread_init(&thread2,

                   "thread2",

                   rt_thread_entry2,

                   RT_NULL,

                   &thread2_stack[0],

                   sizeof(thread2_stack),

                   THREAD_PRIORITY - 1, THREAD_TIMESLICE);

    rt_thread_startup(&thread2);

    return 0;

}

int main(void)

{

    mutex_sample();

    while (1) {

        rt_thread_mdelay(1000);

    }

    return RT_EOK;

}

3、信号量

信号量是一种轻型用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥目的。

/*

* 程序清单:信号量例程

*

* 该例程创建了一个动态信号量,初始化两个线程,线程1在count每计数10次时

* 发送一个信号量,线程2在接收信号量后,对number进行加1操作

*/

#include <rtthread.h>

#define DBG_TAG "main"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

#define THREAD_PRIORITY         25

#define THREAD_TIMESLICE        5

/* 指向信号量的指针 */

static rt_sem_t dynamic_sem = RT_NULL;

ALIGN(RT_ALIGN_SIZE)

static char thread1_stack[1024];

static struct rt_thread thread1;

static void rt_thread1_entry(void *parameter)

{

    static rt_uint8_t count = 0;

    while (1) {

        if (count <= 100) {

            count++;

        } else

            return;

        /* count每计数10次,就释放一次信号量 */

        if (0 == (count % 10)) {

            rt_kprintf("thread1 release a dynamic semaphore.\n");

            rt_sem_release(dynamic_sem);

        }

    }

}

ALIGN(RT_ALIGN_SIZE)

static char thread2_stack[1024];

static struct rt_thread thread2;

static void rt_thread2_entry(void *parameter)

{

    static rt_err_t result;

    static rt_uint8_t number = 0;

    while (1) {

        /* 永久方式等待信号量,获取到信号量,则执行number自加的操作 */

        result = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER);

        if (result != RT_EOK) {

            rt_kprintf("thread2 take a dynamic semaphore, failed.\n");

            rt_sem_delete(dynamic_sem);

            return;

        } else {

            number++;

            rt_kprintf("thread2 take a dynamic semaphore. number = %d\n", number);

        }

    }

}

/* 信号量示例的初始化 */

int semaphore_sample()

{

    /* 创建一个动态信号量,初始值是0 */

    dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_FIFO);

    if (dynamic_sem == RT_NULL) {

        rt_kprintf("create dynamic semaphore failed.\n");

        return -1;

    } else {

        rt_kprintf("create done. dynamic semaphore value = 0.\n");

    }

    rt_thread_init(&thread1,

                   "thread1",

                   rt_thread1_entry,

                   RT_NULL,

                   &thread1_stack[0],

                   sizeof(thread1_stack),

                   THREAD_PRIORITY, THREAD_TIMESLICE);

    rt_thread_startup(&thread1);

    rt_thread_init(&thread2,

                   "thread2",

                   rt_thread2_entry,

                   RT_NULL,

                   &thread2_stack[0],

                   sizeof(thread2_stack),

                   THREAD_PRIORITY - 1, THREAD_TIMESLICE);

    rt_thread_startup(&thread2);

    return 0;

}

int main(void)

{

    semaphore_sample();

    while (1) {

        rt_thread_mdelay(1000);

    }

    return RT_EOK;

}

4、邮箱

邮箱一次可以传递一个4字节大小的邮件,并且邮箱具备一定的存储功能,能够缓存一定数量的邮件数。

/*

* 程序清单:邮箱例程

*

* 这个程序会创建2个动态线程,一个静态的邮箱对象,其中一个线程往邮箱中发送邮件,

* 一个线程往邮箱中收取邮件。

*/

#include <rtthread.h>

#define DBG_TAG "main"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

#define THREAD_PRIORITY      10

#define THREAD_TIMESLICE     5

/* 邮箱控制块 */

static struct rt_mailbox mb;

/* 用于放邮件的内存池 */

static char mb_pool[128];

static char mb_str1[] = "I'm a mail!";

static char mb_str2[] = "this is another mail!";

static char mb_str3[] = "over";

ALIGN(RT_ALIGN_SIZE)

static char thread1_stack[1024];

static struct rt_thread thread1;

/* 线程1入口 */

static void thread1_entry(void *parameter)

{

    char *str;

    while (1) {

        rt_kprintf("thread1: try to recv a mail\n");

        /* 从邮箱中收取邮件 */

        if (rt_mb_recv(&mb, (rt_uint32_t *)&str, RT_WAITING_FOREVER) == RT_EOK) {

            rt_kprintf("thread1: get a mail from mailbox, the content:%s\n", str);

            if (str == mb_str3)

                break;

            /* 延时100ms */

            rt_thread_mdelay(100);

        }

    }

    /* 执行邮箱对象脱离 */

    rt_mb_detach(&mb);

}

ALIGN(RT_ALIGN_SIZE)

static char thread2_stack[1024];

static struct rt_thread thread2;

/* 线程2入口 */

static void thread2_entry(void *parameter)

{

    rt_uint8_t count;

    count = 0;

    while (count < 10) {

        count ++;

        if (count & 0x1) {

            /* 发送mb_str1地址到邮箱中 */

            rt_mb_send(&mb, (rt_uint32_t)&mb_str1);

        } else {

            /* 发送mb_str2地址到邮箱中 */

            rt_mb_send(&mb, (rt_uint32_t)&mb_str2);

        }

        /* 延时200ms */

        rt_thread_mdelay(200);

    }

    /* 发送邮件告诉线程1,线程2已经运行结束 */

    rt_mb_send(&mb, (rt_uint32_t)&mb_str3);

}

int mailbox_sample(void)

{

    rt_err_t result;

    /* 初始化一个mailbox */

    result = rt_mb_init(&mb,

                        "mbt",                      /* 名称是mbt */

                        &mb_pool[0],                /* 邮箱用到的内存池是mb_pool */

                        sizeof(mb_pool) / 4,        /* 邮箱中的邮件数目,因为一封邮件占4字节 */

                        RT_IPC_FLAG_FIFO);          /* 采用FIFO方式进行线程等待 */

    if (result != RT_EOK) {

        rt_kprintf("init mailbox failed.\n");

        return -1;

    }

    rt_thread_init(&thread1,

                   "thread1",

                   thread1_entry,

                   RT_NULL,

                   &thread1_stack[0],

                   sizeof(thread1_stack),

                   THREAD_PRIORITY, THREAD_TIMESLICE);

    rt_thread_startup(&thread1);

    rt_thread_init(&thread2,

                   "thread2",

                   thread2_entry,

                   RT_NULL,

                   &thread2_stack[0],

                   sizeof(thread2_stack),

                   THREAD_PRIORITY, THREAD_TIMESLICE);

    rt_thread_startup(&thread2);

    return 0;

}

int main(void)

{

    mailbox_sample();

    while (1) {

        rt_thread_mdelay(1000);

    }

    return RT_EOK;

}

5、消息队列

消息队列能够接收来自线程或中断服务例程中不固定长度的消息,并把消息缓存在自己的内存空间中。

/*

* 程序清单:消息队列例程

*

* 这个程序会创建2个动态线程,一个线程会从消息队列中收取消息;一个线程会定时给消

* 息队列发送 普通消息和紧急消息。

*/

#include <rtthread.h>

#define DBG_TAG "main"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

#define THREAD_PRIORITY      25

#define THREAD_TIMESLICE     5

/* 消息队列控制块 */

static struct rt_messagequeue mq;

/* 消息队列中用到的放置消息的内存池 */

static rt_uint8_t msg_pool[2048];

ALIGN(RT_ALIGN_SIZE)

static char thread1_stack[1024];

static struct rt_thread thread1;

/* 线程1入口函数 */

static void thread1_entry(void *parameter)

{

    char buf = 0;

    rt_uint8_t cnt = 0;

    while (1) {

        /* 从消息队列中接收消息 */

        if (rt_mq_recv(&mq, &buf, sizeof(buf), RT_WAITING_FOREVER) == RT_EOK) {

            rt_kprintf("thread1: recv msg from msg queue, the content:%c\n", buf);

            if (cnt == 19) {

                break;

            }

        }

        /* 延时50ms */

        cnt++;

        rt_thread_mdelay(50);

    }

    rt_kprintf("thread1: detach mq \n");

    rt_mq_detach(&mq);

}

ALIGN(RT_ALIGN_SIZE)

static char thread2_stack[1024];

static struct rt_thread thread2;

/* 线程2入口 */

static void thread2_entry(void *parameter)

{

    int result;

    char buf = 'A';

    rt_uint8_t cnt = 0;

    while (1) {

        if (cnt == 8) {

            /* 发送紧急消息到消息队列中 */

            result = rt_mq_urgent(&mq, &buf, 1);

            if (result != RT_EOK) {

                rt_kprintf("rt_mq_urgent ERR\n");

            } else {

                rt_kprintf("thread2: send urgent message - %c\n", buf);

            }

        }

        else if (cnt >= 20) {

            /* 发送20次消息之后退出 */

            rt_kprintf("message queue stop send, thread2 quit\n");

            break;

        } else {

            /* 发送消息到消息队列中 */

            result = rt_mq_send(&mq, &buf, 1);

            if (result != RT_EOK) {

                rt_kprintf("rt_mq_send ERR\n");

            }

            rt_kprintf("thread2: send message - %c\n", buf);

        }

        buf++;

        cnt++;

        /* 延时5ms */

        rt_thread_mdelay(5);

    }

}

/* 消息队列示例的初始化 */

int msgq_sample(void)

{

    rt_err_t result;

    /* 初始化消息队列 */

    result = rt_mq_init(&mq,

                        "mqt",

                        &msg_pool[0],               /* 内存池指向msg_pool */

                        1,                          /* 每个消息的大小是 1 字节 */

                        sizeof(msg_pool),           /* 内存池的大小是msg_pool的大小 */

                        RT_IPC_FLAG_FIFO);          /* 如果有多个线程等待,按照先来先得到的方法分配消息 */

    if (result != RT_EOK) {

        rt_kprintf("init message queue failed.\n");

        return -1;

    }

    rt_thread_init(&thread1,

                   "thread1",

                   thread1_entry,

                   RT_NULL,

                   &thread1_stack[0],

                   sizeof(thread1_stack),

                   THREAD_PRIORITY, THREAD_TIMESLICE);

    rt_thread_startup(&thread1);

    rt_thread_init(&thread2,

                   "thread2",

                   thread2_entry,

                   RT_NULL,

                   &thread2_stack[0],

                   sizeof(thread2_stack),

                   THREAD_PRIORITY, THREAD_TIMESLICE);

    rt_thread_startup(&thread2);

    return 0;

}

int main(void)

{

    msgq_sample();

    while (1) {

        rt_thread_mdelay(1000);

    }

    return RT_EOK;

}

五、RT-Thread中断管理

RT-Thread的中断管理功能主要是管理中断设备、中断服务例程、中断嵌套、中断栈的维护、 线程切换时的现场保护和恢复等;

当CPU正在处理内部数据时,外界发生了紧急情况,要求CPU暂停当前的工作转去处理这个异步事件,处理完毕后,再回到原来被中断的地址,继续原来的工作,这样的过程称为中断,实现这一功能的系统称为中断系统,申请CPU中断的请求源称为中断源,当多个中断源同时向CPU请求中断时,就存在一个中断优先权的问题,通常根据中断源的优先级别,优先处理最紧急事件的中断请求源,即最先响应级别最高的中断请求;

当中断产生时,CPU将按如下的顺序执行:

(1)保存当前处理机状态信息;

(2)载入异常或中断处理函数到PC寄存器;

(3)把控制权转交给处理函数并开始执行;

(4)当处理函数执行完成时,恢复处理器状态信息;

(5)从异常或中断中返回到前一个程序执行点;

中断使得CPU可以在事件发生时才进行处理,而不必让CPU连续不断地查询是否有相应的事件发生。

/* 程序清单:关闭中断进行全局变量的访问 */

#include <rtthread.h>

#include <rthw.h>

#define DBG_TAG "main"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

#define THREAD_PRIORITY      20

#define THREAD_STACK_SIZE    512

#define THREAD_TIMESLICE     5

/* 同时访问的全局变量 */

static rt_uint32_t cnt;

void thread_entry(void *parameter)

{

    rt_uint32_t no;

    rt_uint32_t level;

    no = (rt_uint32_t) parameter;

    while (1) {

        /* 关闭中断 */

        level = rt_hw_interrupt_disable();

        cnt += no;

        /* 恢复中断 */

        rt_hw_interrupt_enable(level);

        rt_kprintf("protect thread[%d]'s counter is %d\n", no, cnt);

        rt_thread_mdelay(no * 10);

    }

}

int interrupt_sample(void)

{

    rt_thread_t thread;

    /* 创建thread1线程 */

    thread = rt_thread_create("thread1", thread_entry, (void *)10,

                              THREAD_STACK_SIZE,

                              THREAD_PRIORITY, THREAD_TIMESLICE);

    if (thread != RT_NULL)

        rt_thread_startup(thread);

    /* 创建thread2线程 */

    thread = rt_thread_create("thread2", thread_entry, (void *)20,

                              THREAD_STACK_SIZE,

                              THREAD_PRIORITY, THREAD_TIMESLICE);

    if (thread != RT_NULL)

        rt_thread_startup(thread);

    return 0;

}

int main(void)

{

    interrupt_sample();

    while (1) {

        rt_thread_mdelay(1000);

    }

    return RT_EOK;

}

六、RT-Thread内存管理

1、动态内存

动态内存管理是一个真实的堆内存管理模块,可以在当前资源满足的情况下,根据用户 的需求分配任意大小的内存块,而当用户不需要再使用这些内存块时,又可以释放回堆中供其他应用分配使用;

RT-Thread 系统为了满足不同的需求,提供了两套不同的动态内存 管理算法,分别是小堆内存管理算法和 SLAB 内存管理算法;

小堆内存管理模块主要针对系统资源比较少,一般用于小于2MB内存空间的系统;而SLAB 内存管理模块则主要是在系统资源比较丰富时,提供了一种近似多内存池管理算法的快速算法。

/*

* 程序清单:动态内存管理例程

*

* 这个程序会创建一个动态的线程,这个线程会动态申请内存并释放

* 每次申请更大的内存,当申请不到的时候就结束

*/

#include <rtthread.h>

#define DBG_TAG "main"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

#define THREAD_PRIORITY      25

#define THREAD_STACK_SIZE    512

#define THREAD_TIMESLICE     5

/* 线程入口 */

void thread1_entry(void *parameter)

{

    int i;S

    char *ptr = RT_NULL; /* 内存块的指针 */

    for (i = 0; ; i++) {

        /* 每次分配 (1 << i) 大小字节数的内存空间 */

        ptr = rt_malloc(1 << i);

        /* 如果分配成功 */

        if (ptr != RT_NULL) {

            rt_kprintf("get memory :%d byte\n", (1 << i));

            /* 释放内存块 */

            rt_free(ptr);

            rt_kprintf("free memory :%d byte\n", (1 << i));

            ptr = RT_NULL;

        } else {

            rt_kprintf("try to get %d byte memory failed!\n", (1 << i));

            return;

        }

    }

}

int dynmem_sample(void)

{

    rt_thread_t tid = RT_NULL;

    /* 创建线程1 */

    tid = rt_thread_create("thread1",

                           thread1_entry, RT_NULL,

                           THREAD_STACK_SIZE,

                           THREAD_PRIORITY,

                           THREAD_TIMESLICE);

    if (tid != RT_NULL)

        rt_thread_startup(tid);

    return 0;

}

int main(void)

{

    dynmem_sample();

    while (1) {

        rt_thread_mdelay(1000);

    }

    return RT_EOK;

}

2、内存池

内存池(Memory Pool)是一种内存分配方式,用于分配大量大小相同的小内存块;

它可以极大地加快内存分配与释放的速度,且能尽量避免内存碎片化;

当内存池为空时,可以阻止分配的线程(或者立即返回,或者等待一段时间返回,这由timeout参数确定),当其他线程向此内存池释放内存块时,被阻塞的线程将被唤醒。

/*

* 程序清单:内存池例程

*

* 这个程序会创建一个静态的内存池对象,2个动态线程。

* 一个线程会试图从内存池中获得内存块,另一个线程释放内存块

* 内存块

*/

#include <rtthread.h>

#define DBG_TAG "main"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

static rt_uint8_t *ptr[50];

static rt_uint8_t mempool[4096];

static struct rt_mempool mp;

#define THREAD_PRIORITY      25

#define THREAD_STACK_SIZE    512

#define THREAD_TIMESLICE     5

/* 指向线程控制块的指针 */

static rt_thread_t tid1 = RT_NULL;

static rt_thread_t tid2 = RT_NULL;

/* 线程1入口 */

static void thread1_mp_alloc(void *parameter)

{

    int i;

    for (i = 0 ; i < 50 ; i++) {

        if (ptr[i] == RT_NULL) {

            /* 试图申请内存块50次,当申请不到内存块时,

               线程1挂起,转至线程2运行 */

            ptr[i] = rt_mp_alloc(&mp, RT_WAITING_FOREVER);

            if (ptr[i] != RT_NULL)

                rt_kprintf("allocate No.%d\n", i);

        }

    }

}

/* 线程2入口,线程2的优先级比线程1低,应该线程1先获得执行。*/

static void thread2_mp_release(void *parameter)

{

    int i;

    rt_kprintf("thread2 try to release block\n");

    for (i = 0; i < 50 ; i++) {

        /* 释放所有分配成功的内存块 */

        if (ptr[i] != RT_NULL) {

            rt_kprintf("release block %d\n", i);

            rt_mp_free(ptr[i]);

            ptr[i] = RT_NULL;

        }

    }

}

int mempool_sample(void)

{

    int i;

    for (i = 0; i < 50; i ++) ptr[i] = RT_NULL;

    /* 初始化内存池对象 */

    rt_mp_init(&mp, "mp1", &mempool[0], sizeof(mempool), 80);

    /* 创建线程1:申请内存池 */

    tid1 = rt_thread_create("thread1", thread1_mp_alloc, RT_NULL,

                            THREAD_STACK_SIZE,

                            THREAD_PRIORITY, THREAD_TIMESLICE);

    if (tid1 != RT_NULL)

        rt_thread_startup(tid1);

    /* 创建线程2:释放内存池*/

    tid2 = rt_thread_create("thread2", thread2_mp_release, RT_NULL,

                            THREAD_STACK_SIZE,

                            THREAD_PRIORITY + 1, THREAD_TIMESLICE);

    if (tid2 != RT_NULL)

        rt_thread_startup(tid2);

    return 0;

}

int main(void)

{

    mempool_sample();

    while (1) {

        rt_thread_mdelay(1000);

    }

    return RT_EOK;

}

3、环形缓冲区

文档地址:https://www.rt-thread.org/document/api/group__ringbuffer.html

在通信程序中,经常使用环形缓冲器作为数据结构来存放通信中发送和接收的数据

环形缓冲区是一个先进先出的循环缓冲区,可以向通信程序提供对缓冲区的互斥访问

存储在缓冲区中的有效数据的开始位置(读指针);

存储在缓冲区中的有效数据的结尾位置(写指针)。

#include <rtthread.h>

#include <ipc/ringbuffer.h>

typedef struct rb_example {

    int a;

    int b;

} rb_example_t;

int ringbuffer_example(void)

{

    rb_example_t data = {

        .a = 1,

        .b = 2,

    };

    struct rt_ringbuffer * rb = rt_ringbuffer_create(sizeof(rb_example_t) * 2);

    RT_ASSERT(rb != RT_NULL);

    rt_kprintf("Put data to   ringbuffer, a: %d b: %d size: %d\n", data.a, data.b, sizeof(data));

    rt_ringbuffer_put(rb, (rt_uint8_t *)&data, sizeof(data));

    rt_kprintf("ringbuffer data size: %d\n", rt_ringbuffer_data_len(rb));

    rb_example_t recv_data;

    rt_size_t recv = rt_ringbuffer_get(rb, (rt_uint8_t *)&recv_data, sizeof(recv_data));

    RT_ASSERT(recv == sizeof(recv_data));

    rt_kprintf("Get data from ringbuffer, a: %d b: %d size: %d\n", recv_data.a, recv_data.b, sizeof(recv_data));

    rt_kprintf("ringbuffer data size: %d\n", rt_ringbuffer_data_len(rb));

    return 0;

}

MSH_CMD_EXPORT(ringbuffer_example, ringbuffer example);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值