RT-Thread线程&设备&通信接口(备忘录)
Posted Rabbit-susu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RT-Thread线程&设备&通信接口(备忘录)相关的知识,希望对你有一定的参考价值。
RT-Thread线程通信接口
- 线程接口
- 设备接口(device)
- 线程间通信接口
- 线程间同步接口
- 常用数据类型
调用的接口都是固定的,为了方便迅速创建通信接口.
线程接口
#define THREAD_PRIORITY 25
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5
rt_thread_yield();/*让出CPU资源*/
rt_thread_sleep(ticks);
rt_thread_delay(ticks);
rt_thread_delay(ms);
rt_thread_suspend(xxx_thread);
rt_thread_resume(xxx_thread);
void xxx_thread_entry(void *parameter)
while(1)
static int xxx_task_init(void)
rt_err_t ret = RT_EOK;
/*device初始化*/
/*ipc初始化*/
/* 创建 xxx 线程 --- 动态分配*/
rt_thread_t xxx_thread = rt_thread_create("xxx",
xxx_thread_entry,
RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY,
THREAD_TIMESLICE);
/* 创建成功则启动线程 */
if (thread != RT_NULL)
rt_thread_startup(xxx_thread);
else
ret = RT_ERROR;
/*创建线程 --- 静态分配*/
ALIGN(RT_ALIGN_SIZE)
static char xxx_thread_stack[THREAD_STACK_SIZE];
static struct rt_thread xxx_thread;
rt_thread_init(xxx_thread,
"xxx",
xxx_thread_entry,
RT_NULL,
&xxx_thread_stack[0],
THREAD_STACK_SIZE,
THREAD_PRIORITY - 1, THREAD_TIMESLICE);
rt_thread_startup(&xxx_thread);
INIT_APP_EXPORT(xxx_task_init);
- 注意:
线程必须加入while(1),不然执行一遍后会调用rt_thread_exit(),进入exit状态,相当于执行了rt_thread_delete/detach().
设备接口(device)
/*device flag*/
#define RT_DEVICE_FLAG_RDONLY 0x001 /**< read only */
#define RT_DEVICE_FLAG_WRONLY 0x002 /**< write only */
#define RT_DEVICE_FLAG_RDWR 0x003 /**< read and write */
#define RT_DEVICE_FLAG_INT_RX 0x100 /**< INT mode on Rx */
#define RT_DEVICE_FLAG_DMA_RX 0x200 /**< DMA mode on Rx */
#define RT_DEVICE_FLAG_INT_TX 0x400 /**< INT mode on Tx */
#define RT_DEVICE_FLAG_DMA_TX 0x800 /**< DMA mode on Tx */
#define RT_DEVICE_FLAG_STREAM 0x040 /**< stream mode */
/*cmd--device config*/
#define RT_DEVICE_CTRL_CONFIG 0x03 /**< configure device */
static rt_err_t xxx_input(rt_device_t dev, rt_size_t size);
struct serial_configure config= ;/*串口的配置结构体*/
char str[XXX_RB_BUFSZ+1];
/****************设备操作****************/
rt_device_t xxx_device = NULL;
xxx_device = rt_device_find("xxx");
rt_device_control(xxx_device,cmd,param)
ret = rt_device_open(xxx_device,flag);
rt_device_set_rx_indicate(xxx_device,xxx_input)
/*tips回调函数包含dev*/
rt_device_write(xxx_device,pos,str,sizeof(str)-1)
length = rt_device_read(xxx_device,pos,str,size)
线程间通信接口
线程间通信
线程间通信的接口都提供了两种初始化方式 —— 动态和静态.
它们的接口有如下关系:
动态的创建和删除:xxx_create()/xxx_delete()
静态的创建和解绑:xxx_init()/xxx_detach()
typedef struct rt_mailbox* rt_mailbox_t;
#define XXX_MB_BUFSZ 64
#define XXX_STR_BUFSZ 32
/*************** 静态创建 ********************/
/* 邮箱控制块 */
static struct rt_mailbox xxx_mb;
/* 用于放邮件的内存池 */
static char mb_pool[XXX_MB_BUFSZ];
static char mb_str[XXX_STR_BUFSZ] = "I\'m a mail!"
static char *str
result = rt_mb_init(&xxx_mb,
"xxx_mb", /* 名称是 mbt */
&mb_pool[0], /* 邮箱用到的内存池是 mb_pool */
sizeof(mb_pool) / 4, /* 邮箱中的邮件数目,因为一封邮件占 4 字节 */
RT_IPC_FLAG_FIFO); /* 采用 FIFO 方式进行线程等待 */
/*************** 动态创建 ********************/
rt_mailbox_t xxx_mb = RT_NULL;
xxx_mb = rt_mb_create("xxx_mb",sizeof(mb_pool) / 4,RT_IPC_FLAG_FIFO)
/* xxx_thread发送 */
rt_mb_send(&mb, (rt_uint32_t)&mb_str)
/* yyy_thread接收 */
rt_mb_recv(&mb, (rt_uint32_t *)&str, RT_WAITING_FOREVER)
- 邮箱用法
邮箱只能传4字节的数据,通常用邮箱传数据结构的地址,比如,数组的地址,结构体的地址等.
typedef struct rt_messagequeue* rt_mq_t;
#define XXX_MQ_BUFSZ 2048
/*************** 动态创建 ********************/
rt_mq_t xxx_mq = RT_NULL;
xxx_mq = rt_mq_create("xxx_mq",sizeof(msg_pool),RT_IPC_FLAG_FIFO);
/*************** 静态创建 ********************/
/* 消息队列控制块 */
static struct rt_messagequeue xxx_mq;
/* 消息队列中用到的放置消息的内存池 */
static rt_uint8_t msg_pool[XXX_MQ_BUFSZ];
/* 初始化消息队列 */
result = rt_mq_init(&xxx_mq,
"xxx_mq",
&msg_pool[0], /* 内存池指向 msg_pool */
1, /* 每个消息的大小是 1 字节 */
sizeof(msg_pool), /* 内存池的大小是 msg_pool 的大小 */
RT_IPC_FLAG_PRIO); /* 如果有多个线程等待,优先级大小的方法分配消息 */
/*xxx_thread中 发送消息到消息队列中 */
result = rt_mq_send(&xxx_mq, &buf, 1);/*发送一个字节....*/
/*yyy_thread */
ret = rt_mq_recv(&xxx_mq, &buf, sizeof(buf), RT_WAITING_FOREVER)
- 消息队列的用法
struct msg
rt_uint8_t *data_ptr; /* 数据块首地址 */
rt_uint32_t data_size; /* 数据块大小 */
;
void send_op(void *data, rt_size_t length)
struct msg msg_ptr;
msg_ptr.data_ptr = data; /* 指向相应的数据块地址 */
msg_ptr.data_size = length; /* 数据块的长度 */
/* 发送这个消息指针给 mq 消息队列 */
rt_mq_send(mq, (void*)&msg_ptr, sizeof(struct msg));
void message_handler()
struct msg msg_ptr; /* 用于放置消息的局部变量 */
/* 从消息队列中接收消息到 msg_ptr 中 */
if (rt_mq_recv(mq, (void*)&msg_ptr, sizeof(struct msg), RT_WAITING_FOREVER) == RT_EOK)
/* 成功接收到消息,进行相应的数据处理 */
struct msg
/* 消息结构其他成员 */
struct rt_mailbox ack;
/* 消息的数据成员 */
rt_uint8_t *data_ptr; /* 数据块首地址 */
rt_uint32_t data_size; /* 数据块大小 */
;
/* 或者 */
struct msg
/* 消息结构其他成员 */
struct rt_semaphore ack;
/* 消息的数据成员 */
rt_uint8_t *data_ptr; /* 数据块首地址 */
rt_uint32_t data_size; /* 数据块大小 */
;
#define XXX_MSG_BUFSZ 512
struct rt_mailbox xxx_mb;
rt_uint8_t xxx_data_pool[XXX_MSG_BUFSZ]
struct msg =
.ack = xxx_mb
.data = xxx_data_pool;
.data_size = XXX_MSG_BUFSZ;
/* xxx_thread发送消息 */
send_op(&msg,sizeof(msg))
/* 使用mailbox作为ack */
ret = rt_mq_recv(xxx_mb,&ack_state, 1, timeout);
/* 使用信号量作为ack */
ret = rt_sem_take(xxx_sem, timeout);/*val--*/
/* 超时 */
if(ret = -RT_ETIMEOUT)
/* 超时处理 */
else
/*ack正常处理*/
/* yyy_thread接收消息 */
void message_handler()
struct msg msg_ptr; /* 用于放置消息的局部变量 */
int state = 0;
/* 从消息队列中接收消息到 msg_ptr 中 */
if (rt_mq_recv(mq, (void*)&msg_ptr, sizeof(struct msg), RT_WAITING_FOREVER) == RT_EOK)
/* 成功接收到消息,进行相应的数据处理 */
/* 使用msg_ptr->data_ptr & msg_ptr->data_size的数据 */
rt_mq_send(msg_ptr->ack, state, 1);
/* 信号量同样 */
rt_sem_release(msg_ptr->ack);/*val++*/
线程间同步接口
rt_enter_critical()
rt_exit_critical()
typedef struct rt_semaphore* rt_sem_t;
/*************** 动态创建 ********************/
rt_err_t result = RT_EOK;
rt_sem_t xxx_sem= RT_NULL;
/* 创建信号量--基于优先级 */
xxx_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_PRIO)
/* 在yyy线程(优先级N)释放信号量 */
rt_sem_release(xxx_sem);/* val++ */
/* 在xxx线程(优先级N-1)获取信号量,此时xxx线程与yyy线程同步 */
result = rt_sem_take(xxx_sem, RT_WAITING_FOREVER);/* val-- */
if (result != RT_EOK)
rt_sem_delete(xxx_sem)/* 删除信号 */
效果:
/* 在yyy线程(优先级N)释放信号量 */
/* code 1 */
rt_sem_release(xxx_sem);/* val++ */
/* code 2 */
/* 在xxx线程(优先级N-1)获取信号量,此时xxx线程与yyy线程同步 */
result = rt_sem_take(xxx_sem, RT_WAITING_FOREVER);/* val-- */
if (result != RT_EOK)
rt_sem_delete(xxx_sem)/* 删除信号 */
/* code 3 */
/***********************************
* 执行顺序:code 1 -> code 3 -> code 2
************************************/
/*************** 静态创建 ********************/
struct rt_semaphore xxx_sem;
rt_sem_init(&xxx_sem, "xxx", 1, RT_IPC_FLAG_PRIO);/* 允许访问一次 */
/* 原子操作 */
rt_sem_take(xxx_sem, RT_WAITING_FOREVER);
/* 资源M */
rt_sem_release(xxx_sem);
/* xxx线程,yyy线程的优先级不同,不能相差>=2,否则会有优先级反转的问题 */
- 注意:
信号量的问题:优先级反转
优先级A>B>C,A和C有共享资源(M)时,当C占有资源,则A会被挂起,此时B因为不需要访问共享资源(M),所以抢占了C的执行,从而A的实时性得不到保障.
typedef struct rt_mutex* rt_mutex_t;
/*************** 动态创建 ********************/
static rt_mutex_t xxx_mutex = RT_NULL;
/* 基于优先级创建一个mutex */
xxx_mutex = rt_mutex_create("xxx_mutex", RT_IPC_FLAG_PRIO);
/* xxx_thread中,对共享资源M上锁 */
rt_mutex_take(xxx_mutex, RT_WAITING_FOREVER);
/* 资源M */
rt_mutex_release(xxx_mutex);
/* xxx线程,yyy线程优先级不同,可以相差>=2 */
/*************** 静态创建 ********************/
struct rt_mutex = xxx_mutex;
rt_mutex_init(xxx_mutex, "xxx_mutex",RT_IPC_FLAG_PRIO)
常用数据类型
typedef signed long rt_base_t; /**< Nbit CPU related date type */
typedef rt_base_t rt_err_t; /**< Type for error number */
typedef rt_uint32_t rt_time_t; /**< Type for time stamp */
typedef rt_uint32_t rt_tick_t; /**< Type for tick count */
typedef rt_base_t rt_flag_t; /**< Type for flags */
typedef rt_ubase_t rt_dev_t; /**< Type for device */
typedef rt_base_t rt_off_t; /**< Type for offset */
本文来自博客园,作者:当最后一片树叶落下,转载请注明原文链接:https://www.cnblogs.com/Rabbit-susu/p/17367261.html
rt-thread 线程间同步和通信你用对了吗
本文由RT-Thread论坛用户@出出啊原创发布:https://club.rt-thread.org/ask/article/2957.html
前言
系统优化系列先停一停,总对人指指点点会让大家反感的。今天给各位 rt-thread 使用者一些使用信号量、邮箱、消息队列等同步和通信机制的建议。
线程间同步概念
摘取 RT-Thread 官方文档中心对“线程间同步”的讲解。
同步是指按预定的先后次序进行运行,线程同步是指多个线程通过特定的机制(如互斥量,事件对象,临界区)来控制线程之间的执行顺序,也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间将是无序的。
多个线程操作 / 访问同一块区域(代码),这块代码就称为临界区,上述例子中的共享内存块就是临界区。线程互斥是指对于临界区资源访问的排它性。当多个线程都要使用临界区资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步。
线程间同步的方式包括但不限于信号量、互斥量、事件集等。
线程间通信概念
说线程间同步使得不同线程的执行变得有序、有规有矩,避免了临界区的竞争访问。那么,线程间通信让多线程之间更“智能”。
线程间通信的方式包括但不限于邮箱、消息队列、信号
两种使用场景
毋庸置疑的,所有同步和通信机制都是为线程(任务)而生的。没有同步和通信的多线程是一盘散沙。在线程里调用同步和通信 API ,这是第一种使用场景。
第二种使用场景:中断向线程发送信号量、事件集、邮箱、消息队列。(互斥量千万不要在中断里使用)
我想,多数人使用过中断向线程 release / send 信号量或消息队列。从业务逻辑上讲,把中断等价于一个“线程”给另一个线程发送信号量等也是说得过去的。
但是,多数使用者忽略了一个问题,那就是,线程间同步和通信机制发送 API 无一例外会调用 rt_schedule 函数(任务调度)。虽然在中断里调用 rt_schedule 不会立马切换线程。但是,这无疑是增加中断处理任务量的!另外,还有一个值得需要考虑的问题,如果立马进行任务调度,有没有从高优先级切换到低优先级线程的风险?
由此,可能引起某些非预期后果,下面分两种情况分析。
当中断频度低,或者系统中断类型比较少的情况下,中断处理时间长短对业务影响不是很明显。例如行程开关的gpio中断,秒级或者分钟级时间周期才产生一次中断。
当中断频度高,或者系统中有多个比较频繁的中断类型。中断处理时间过长,可能引起同(低)优先级中断丢失。例如 115200 波特率的串口中断里使用 rt_sem_release 或者 rt_mq_send 时,得考虑一个问题了,串口接收中断处理函数最大处理时间超过 86us 了吗?
通信机制虽好,请勿随意使用。
中断接收处理的几点建议
个人建议,在中断中使用信号量或消息队列等机制时先考虑一下
中断频度在 10ms 以下,谨慎使用,1ms 以下就考虑其它方式吧。
开启 DMA ,DMA 是个好东西,因为有缓存,对中断处理的任务量要求小很多。
使用原子类型做数据非空标识。
可惜目前 rt-thread 没有提供原子类型的实现接口,不过我们可以用整型变量 f 代替(多数芯片架构下是可行的),中断接收数据放到用户 fifo 里,同时置位变量 f。线程里轮询变量 f 是否改变,检测到改变后立马复位变量 f,然后获取 filo 数据量,出栈数据。
可以保证的是,检测 f 是否改变不需要关中断,复位 f 时才需要短时间关中断,fifo 出栈数据也有极短的关中断时间。这些都比进行任务调度的 cpu 开销少。唯一不足是轮询检测的过程占用 cpu 高,怎么降低轮询的 cpu 占用?可以尝试一下 rt_thread_yield rt_schedule 。
总结
线程间同步和线程间通信机制虽好,但是并不是什么时候都能使用的。我们都知道中断里不能使用互斥量,但是忽略了,高频中断里使用同步和通信带来的隐形影响。
rtos 并没有让中断的使用更方便,相反,相比裸机,增加了更多注意点和使用限制。
互相分享,共同进步!充电完毕后,也别忘记来分享噢,RT-Thread每月都有原创文章征集活动,鼓励大家多多分享技术文章!
以上是关于RT-Thread线程&设备&通信接口(备忘录)的主要内容,如果未能解决你的问题,请参考以下文章