基于TI-RTOS的CC2650DK开发(14)---邮箱

judy的头像

Mailboxes(邮箱)

ti.sysbios.knl.Mailbox模块提供了一系列函数管理mailboxes。Mailboxes可用于在相同处理器的两个task中传递缓冲。一个Mailbox实例可用于多个readers和writers。

Mailbox模块拷贝缓冲到合适尺寸的内部缓冲。这些缓冲的尺寸和个数在Mailbox实例创建时指定。拷贝在通过Mailbox_post()发送缓冲时建立。另外一份拷贝在通过Mailbox_pend()接收缓冲时建立。

Mailbox_create()和Mailbox_delete()分别用于创建和删除mailboxes。你也可以静态地创建mailbox对象。Mailbox可用于确保输入缓冲流不会超过系统处理那些缓冲的能力。本节稍后给出的例子演示了这种设计。

在创建mailbox时,你可以为每个缓冲指定其尺寸以及内部mailbox缓冲的个数。在创建Mailbox时确定尺寸之后,所有Mailbox的发送和接收缓冲必须使用这个相同的尺寸。

Mailbox_Handle Mailbox_create(SizeT bufsize,
                              UInt numBufs,
                              Mailbox_Params *params,
                              Error_Block *eb)
Void Mailbox_delete(Mailbox_Handle *handle);

Mailbox_pend()用于从mailbox中读取一个缓冲。如果无缓冲可用(也就是mailbox是空的),Mailbox_pend()阻塞。timeout参数为允许task等待的时间,无限期等待(BIOS_WAIT_FOREVER),或根本不等待(BIOS_NO_WAIT)。时间单位为系统时钟ticks。

Bool Mailbox_pend(Mailbox_Handle handle,
                  Ptr buf,
                  UInt timeout);

Mailbox_post()用于提交缓冲给mailbox。如果无缓冲槽可用(也就是mailbox满了),Mailbox_post()阻塞。timeout参数为允许task等待的时间,无限期等待(BIOS_WAIT_FOREVER),或根本不等待(BIOS_NO_WAIT)。

Bool Mailbox_post(Mailbox_Handle handle,
                  Ptr buf,
                  UInt timeout);

Mailbox提供了配置参数允许你将事件关联到mailbox。这使得你可以在同一时间可等待mailbox消息和其它事件。Mailbox提供两个配置参数以支持mailbox的reader的事件----readerEvent和readerEventId。这使得mailbox reader可以使用事件对象去等待mailbox消息。Mailbox还提供了两个配置参数用于mailbox writer---writerEvent和writerEventId。这使得mailbox writer可使用事件对象等待mailbox的room(room可理解为缓冲槽)。

注意,这些事件句柄的名称可能会有误导性。readerEvent是Mailbox reader请求的事件,但它是在Mailbox_post()调用中被Mailbox writer提交的。writeEvent是Mailbox writer请求等待Mailbox有空位的事件,所以在Mailbox不满时它可以成功执行Mailbox_post()。无论何时,只要Mailbox成功读取,Mailbox reader都会提交writerEvent(也就是说Mailbox_pend()返回TRUE)。

当使用事件时,一个线程调用Event_pend()并等待数个事件。在从Event_pend()返回的时候,线程必须调用Mailbox_pend()或Mailbox_post()---取决于它是reader还是writer---timeout参数为BIOS_NO_WAIT。参考4.2.1节的代码实例,它包含返回Event_pend()后的相应的Mailbox资源。

Queues(队列)

ti.sysbios.knl.Queue模块提供了对创建对象列表的支持。队列实现为双向链表,所以元素在列表中可以插入和删除,另外,队列没最大尺寸限制。

基础FIFO(先进先出)队列操作

添加一个结构体进入队列,第一个字段需要是Queue_Elem类型。下例显示了一个可被加入Queue的结构体。

队列有一个“头”,在列表的最前端。Queue_enqueue()将元素添加到列表未尾,Queue_dequeue()移除并返回列表前端元素,这些函数支持队列的FIFO操作。

运行时例子:下例演示了基于队列的操作---Queue_enqueue()和Queue_dequeue()。它也使用了Queue_empty()函数,当队列中没有元素时返回true。

/* This structure can be added to a Queue because the first field is a Queue_Elem. */
typedef struct Rec
{
    Queue_Elem elem;
    Int data;
} Rec;
Queue_Handle myQ;
Rec r1, r2;
Rec *rp;
r1.data = 100;
r2.data = 200;
// No parameters or Error block are needed to create a Queue.
myQ = Queue_create(NULL, NULL);
// Add r1 and r2 to the back of myQ.
Queue_enqueue(myQ, &(r1.elem));
Queue_enqueue(myQ, &(r2.elem));
// Dequeue the records and print their data
while (!Queue_empty(myQ))
{
    // Implicit cast from (Queue_Elem *) to (Rec *)
    rp = Queue_dequeue(myQ);
    System_printf("rec: %d\n", rp->data);
}

此例打印结果如下:

rec: 100
rec: 200

队列的迭代

队列模块还提供了几个APIs用于循环访问队列。Queue_head()返回队列的前端元素且不移除它,Queue_next()和Queue_prev()分别返回队列中的下一个和前一个元素。

运行时实例:以下代码演示了一种遍历队列的方法。此例中,“myQ”是一个Queue_Handle。

Queue_Elem *elem;
for (elem = Queue_head(myQ); elem != (Queue_Elem *)myQ;
        elem = Queue_next(elem))
{
    ...
}

插入和移除队列元素

使用Queue_insert()和Queue_remove()可以在队列中的任何位置插入或移除元素。Queue_insert()在指定元素前插入一个元素,Queue_remove()删除一个指定元素,无论它在队列中的哪个位置。注意,队列不提供任何APIs用于在给定索引入插入或移除元素。

运行时例子:下例演示了Queue_insert()和Queue_remove()。

Queue_enqueue(myQ, &(r1.elem));
/* Insert r2 in front of r1 in the Queue. */
Queue_insert(&(r1.elem), &(r2.elem));
/* Remove r1 from the Queue. Note that Queue_remove() does not
* require a handle to myQ. */
Queue_remove(&(r1.elem));

原子队列操作

队列通常在系统中通常由多个线程共享,这可能导致不同线程同时更改队列,这将破坏队列。上述讨论的队列APIs不保护这些。然而,队列提供了两个原子APIs,它可以在操作队列前禁用中断。这些APIs是Queue_get(),它是Queue_dequeue()和Queue_put()的原子操作版本,也是Queue_enqueue()的原子版本。

出处: abatei的专栏