欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

RT-Thread 线程同步及通信

程序员文章站 2022-07-11 18:56:13
...

目录

一  RT-Thread 信号量

二  RT-Thread互斥量

三  RT-Thread事件标志组


一  RT-Thread 信号量

1. 信号量相关函数

创建信号量

/** @param  name:信号量名称

  * @param  value:信号量初始值

  * @param  flag:信号量标志,可取RT_IPC_FLAG_FIFO或者RT_IPC_FLAG_PRIO
  * @retval  创建成功返回创建的信号量控制块指针;否则返回RT_NULL
  */

rt_sem_t rt_sem_create (const char* name,rt_uint32_t value,rt_uint8_t flag);

删除信号量

/** @param  sem:rt_sem_create创建处理的信号量对象
  * @retval  RT_EOK
  */

rt_err_t rt_sem_delete (rt_sem_t sem);

初始化静态信号量

/** @param  sem:信号量对象的句柄

  * @param  name:信号量名称

  * @param  value:信号量初始值

  * @param  flag:信号量标志,可取RT_IPC_FLAG_FIFO或者RT_IPC_FLAG_PRIO
  * @retval  RT_EOK
  */

rt_err_t rt_sem_init (rt_sem_t sem, const char* name, rt_uint32_t value, rt_uint8_t flag);

脱离信号量

/** @brief   让信号量对象从内核对象管理器中移除掉

  * @param  sem:rt_sem_create创建处理的信号量对象
  * @retval  RT_EOK
  */

rt_err_t rt_sem_detach (rt_sem_t sem);

获取信号量

 

/** @brief   线程通过获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号
  *                量,并且相应的信号量值都会减1

  * @param  sem:信号量对象的句柄

  * @param  time:指定的等待时间,单位是操作系统时钟节拍
  * @retval  成功获得信号量返回RT_EOK;超时依然未获得信号量返回-RT_ETIMEOUT;其他错误返
  *                回-RT_ERROR。
  */

rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time);

无等待获取信号量

/** @brief   这个函数与rt_sem_take(sem, 0) 的作用相同

  * @param  sem:信号量对象的句柄
  * @retval  成功获取信号量返回RT_EOK;否则返回RT_ETIMEOUT。
  */

rt_err_t rt_sem_trytake(rt_sem_t sem);

释放信号量

/** @brief   当线程完成资源的访问后,应尽快释放它持有的信号量,使得其他线程能获得该信号量

  * @param  sem:信号量对象的句柄
  * @retval  RT_EOK
  */

rt_err_t rt_sem_release(rt_sem_t sem);

2.  创建一个静态信号量

/* 信号量控制块 */
static struct rt_semaphore sem;

/* 初始化信号量,初始值是1 */
result = rt_sem_init(&sem, "sem", 1, RT_IPC_FLAG_FIFO);

3. 创建一个动态信号量

/* 指向信号量的指针 */
rt_sem_t sem = RT_NULL;

/* 创建一个信号量,初始值是1 */
sem = rt_sem_create("sem", 1, RT_IPC_FLAG_FIFO);

4. 通过信号量访问共享资源,不推荐,可能会导致优先级翻转

rt_sem_take(sem, RT_WAITING_FOREVER);/* 试图持有一个信号量,如果不成功则一直等待知道成功 */
     *
访问共享资源
     *
rt_sem_release(sem); /* 释放一次信号量 */

5. 使用信号量进行中断与线程同步

void USART1_IRQHandler(void)
{
    接受到串口数据
    rt_sem_release(sem); /* 释放信号量 */
}

void thread0(void* arg)
{
    while(1)
    {
        rt_sem_take(sem); /* 获取信号量 */
        处理串口数据
    }
}

二  RT-Thread互斥量

1. 互斥量相关函数

创建互斥量

/**

  * @brief   完成对该互斥量控制块的初始化工作

  * @param  name:互斥量的名称

  * @param  flag:互斥量标志,可取RT_IPC_FLAG_FIFO 与 RT_IPC_FLAG_PRIO
  * @retval  创建成功返回指向互斥量的互斥量句柄;否则返回RT_NULL
  */

rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag);

删除互斥量

/** @param  mutex:互斥量对象的句柄
  * @retval  RT_EOK
  */

rt_err_t rt_mutex_delete (rt_mutex_t mutex);

初始化静态互斥量

/**

  * @param  mutex:互斥量对象的句柄

  * @param  name:互斥量的名称

  * @param  flag:互斥量标志,可取RT_IPC_FLAG_FIFO 与 RT_IPC_FLAG_PRIO
  * @retval  RT_EOK
  */

rt_err_t rt_mutex_init (rt_mutex_t mutex, const char* name, rt_uint8_t flag);

脱离互斥量

/** @brief:脱离互斥量将把互斥量对象从内核对象管理器中删除

  * @param  mutex:互斥量对象的句柄
  * @retval  RT_EOK
  */

rt_err_t rt_mutex_detach (rt_mutex_t mutex);

获取互斥量

/** @brief:线程通过互斥量申请服务获取互斥量的所有权,如果互斥量已经被当前线程线程控制,则该互斥                       量的持有计数加1,

  * @param  mutex:互斥量对象的句柄

  * @param  time:指定等待的时间
  * @retval  成功获得互斥量返回RT_EOK;超时返回-RT_ETIMEOUT;其他返回-RT_ERROR
  */

rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time);

释放互斥量

/** @brief:使用该函数接口时,只有已经拥有互斥量控制权的线程才能释放它,每释放一次该互斥量,它的持                     有计数就减1,当该互斥量的持有计数为零时,它变为可用。

  * @param  mutex:互斥量对象的句柄
  * @retval  RT_EOK
  */

rt_err_t rt_mutex_release(rt_mutex_t mutex);

2. 互斥量注意事项

  • 需要切记的是互斥量不能在中断服务例程中使用。
  • 可以防止优先级翻转
  • 在获得互斥量之后,应该尽快释放互斥量
  • 持有互斥量的过程中,不得再调用 rt_thread_control()等函数接口更改持有互斥量线程的优先级
  • 互斥量可用于对单个硬件资源进行保护,比如多个线程需要调用串口1发送数据,则可以在调用串口时使用互斥量

3. 创建静态互斥量

/* 互斥量控制块*/
static rt_mutex mutex;

/* 初始化互斥量*/
rt_mutex_create(&mutex, "mutex", RT_IPC_FLAG_FIFO);

4.创建动态互斥量

/* 指向互斥量的指针 */
static rt_mutex_t mutex = RT_NULL;

/* 创建互斥锁 */
mutex = rt_mutex_create("mutex", RT_IPC_FLAG_FIFO);

5. 使用互斥量访问共享资源(例如串口),推荐使用该种方式访问共享资源,而不是信号量的方式。

void usart_send_buf(uint8_t *buf,uint32_t len)
{
    rt_mutex_take(mutex, RT_WAITING_FOREVER); /*  获取互斥量 */

    调用硬件串口资源输出字符
    
    rt_mutex_release(mutex);
}

三  RT-Thread事件标志组

1. 事件标志组相关函数

创建事件

/**

  * @brief   对事件控制块进行基本的初始化

  * @param  name:事件的名称

  * @param  flag:事件标志,可取RT_IPC_FLAG_FIFO 与 RT_IPC_FLAG_PRIO
  * @retval  创建成功返回事件对象的句柄;创建失败返回RT_NULL
  */

rt_event_t rt_event_create (const char* name, rt_uint8_t flag);

删除事件

/** @param  event:事件对象的句柄
  * @retval  RT_EOK
  */

rt_err_t rt_event_delete (rt_event_t event);

初始化事件

/**

  * @param  event:事件对象的句柄

  * @param  name:事件的名称

  * @param  flag:事件标志,可取RT_IPC_FLAG_FIFO 与 RT_IPC_FLAG_PRIO
  * @retval  RT_EOK
  */

rt_err_t rt_event_init(rt_event_t event, const char* name, rt_uint8_t flag);

脱离事件

/** @brief:脱离事件是将事件对象从内核对象管理器中删除

  * @param  mutex:事件对象的句柄
  * @retval  RT_EOK
  */

rt_err_t rt_event_detach(rt_event_t event);

接受事件

/** @brief:内核使用32位的无符号整型数来标识事件,它的每一位代表一个事件,因此一个事件对 象可同时等                     待接收32个事件。当用户调用这个接口时,首先根据set参数和接收选项来判断它要接收的事件是否                     发生,如果已经发生,则根据参数option上是否设置有RT_EVENT_FLAG_CLEAR来决定是否重置

                   事件的相应标志位

  * @param  event:事件对象的句柄

  * @param  set:接收线程感兴趣的事件

  * @param  option:接收选项

  * @param  timeout:指定超时时间

  * @param  recved:指向收到的事件
  * @retval  正确接收返回RT_EOK,超时返回-RT_TIMEOUT,其他返回-RT_ERROR
  */

rt_err_t rt_event_recv(rt_event_t event, rt_uint32_t set, rt_uint8_t option,rt_int32_t timeout, rt_uint32_t* recved);

发送事件

/** @brief:可以发送一个或多个事件

  * @param  event:事件对象的句柄

  * @param  set:接收线程感兴趣的事件
  * @retval  RT_EOK
  */

rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);

2. 创建静态事件

/* 事件控制块 */
static struct rt_event event;
/* 初始化事件对象 */
rt_event_init(&event, "event", RT_IPC_FLAG_FIFO);

3. 创建动态事件

/* 指向事件的地址指针 */
rt_event_t event = RT_NULL;
/* 创建事件 */
event = rt_event_create( "event", RT_IPC_FLAG_FIFO);

4. 用于中断与线程的同步

void USART1_IRQHandler(void)
{
    接受到串口数据
    rt_event_send(event,0x1); /*发送事件 */
}

void thread0(void* arg)
{
    rt_uint32_t recved;
    while(1)
    {
        /*  等待接收事件标志 */
        rt_event_recv(test_event,    /*  事件对象句柄 */
			  0x1,                   /*  接收线程感兴趣的事件 */
			  RT_EVENT_FLAG_AND|RT_EVENT_FLAG_CLEAR,/*  接收选项 */
			  RT_WAITING_FOREVER,    /*  指定超时事件, 一直等 */
			  &recved);              /*  指向接收到的事件 */
        ...
        处理串口数据
    }
}