uORB(Micro Object Request Broker,微对象请求代理器)是PX4/Pixhawk系统中非常重要且关键的一个模块,它肩负了整个系统的数据传输任务,所有的传感器数据、GPS、PPM信号等都要从芯片获取后通过uORB进行传输到各个模块进行计算处理。实际上uORB是一套跨「进程」的IPC通讯模块。所有的功能被独立以进程模块为单位进行实现并工作。而进程间的数据交互就由为重要,必须要能够符合实时、有序的特点。uORB实际上是多个进程打开同一个设备文件,进程间通过此文件节点进行数据交互和共享。进程通过命名的「总线」交换的消息称之为「主题」(topic),一个主题仅包含一种消息类型,通俗点就是数据类型。每个进程可以「订阅」或者「发布」主题,可以存在多个发布者,或者一个进程可以订阅多个主题,但是一条总线上始终只有一条消息。
uORB的设计理念很有趣,它可以实现不同模块中的数据快速通讯,并且以异步通讯为基本原则,也就是说在通讯过程中发送者只负责发送数据,而并不关心数据由谁接收,也不关心接收者是否能将所有的数据都接收到;而对于接收者来说并不关心数据是由谁发送的,也不关心在接收过程中是否将所有数据都接收到。关于uORB的设计原理我们会在后续章节中仔细讲述。这里只做一个简单的介绍,我们来举一个简单的例子说明一下uORB的设计原理:
有一个教室编号208,里面的黑板上可以写上一些文字内容,有一个同学名叫小强,他每隔1个小时就会来到208教室,先将黑板上原来的文字擦除,然后在黑板上写下一段新文字,之后离开208教室。而另外有一个同学叫小朋,他每隔3个小时就会来到208教室,将黑板上的文字抄写到自己的笔记本上,然后离开。我们可以用下列图例来说明一下这个过程:
我们可以看到,小强每次发布数据之后就会离开208教室,至于有没有人或是谁来读取他留下的文字,小强自己并不关心,也不再乎自己发布的数据是否有人收到了。而对于小朋来说,他每隔3小时来读取一次数据,至于这些数据是谁发布的他也不关心。他每隔3小时来读黑板上的文字时,其实小强已经在黑板上留言3次了,前两次的文字已经被小强擦除了,小朋看到的永远是小强留下最新的内容。
上面这个例子实际上就是uORB的实现原理:
发送者:小强每隔1小时发布一次数据orb_publish
接收者:小朋每隔3小时接收一次数据orb_copy
uORB在在数据发布与接收过程中并不保证发送者的所有数据都可以被接收者收到,而只保证接收者在想要接收时能收到最新的数据。而发送与接收的分离可以使飞程中各个模块相互独立,互不干扰。实际上一个uORB可以由多个发送者发布,也可以被多个接收者接收。实际上同一个uORB可以由多个发布者进行发布,而也可以由多个接收者接收,也就是说他们之间是多对多的关系。
应用程序框架
rtthread中利用事件实现uorb功能将其作为中间层,运行于操作系统之上,提供设备驱动和一个微对象请求代理(micro object request broker ,uorb)用于飞控上运行的单个任务之间的异步通信。下面是实现方式:
#define EVENT_ORB_DATA_UPDATE 0x02
#define ORB_ID(_name) &__orb_##_name
#define ORB_DECLARE(_name) extern orb_node __orb_##_name
/* orb主题定义 */
typedef struct orb_subscriber
{
void *update;
char subscriber[RT_NAME_MAX];
rt_list_t subscriber;
} orb_subscriber;
/* orb节点定义 */
typedef struct orb_node
{
const char *name;
uint32_t size;
uint32_t updated_num;
uint32_t subscriber_num;
rt_thread_t advertiser;
void *orb_data;
#ifdef UORB_USING_MUTEX
rt_mutex_t node_lock;
#endif
rt_list_t sub_list;
} orb_node;
/**
* 定义(实例化)主题的uORB元数据。
* 注意:对于每个主题,此宏的实例不能超过一个。
* @param _name 主题名。
* @param _struct 发布数据结构体。
*/
#ifdef UORB_USING_MUTEX
#define ORB_DEFINE(_name, _struct) \
orb_node __orb_##_name = { \
#_name, \
sizeof(_struct), \
0, \
0, \
RT_NULL, \
RT_NULL, \
RT_NULL, \
RT_NULL, \
RT_NULL
};
#else
#define ORB_DEFINE(_name, _struct) \
orb_node __orb_##_name = { \
#_name, \
sizeof(_struct), \
0, \
0, \
RT_NULL, \
RT_NULL, \
RT_NULL, \
RT_NULL
};
#endif
/* 发布orb节点主题 */
rt_err_t orb_advertise(orb_node *node)
{
if(node->orb_data == RT_NULL)
{
node->orb_data = rt_malloc(node->size); //申请节点通讯数据空间
node->advertiser = rt_thread_self();
#ifdef UORB_USING_MUTEX
node->node_lock = rt_mutex_create(node->name, RT_IPC_FLAG_FIFO);
#endif
rt_list_init(&node->sub_list); //订阅主题链接初始化
}
}
/* 从节点处订阅ORB主题*/
rt_err_t orb_subscribe(orb_node *node,uint32_t *event)
{
rt_uint32_t level;
struct rt_event *update = RT_NULL;
struct orb_subscriber *new_subscriber = RT_NULL;
char name[RT_NAME_MAX+4] = "orb_";
rt_thread_t self = rt_thread_self();
#ifdef UORB_USING_MUTEX
rt_mutex_take(node->node_lock, RT_WAITING_FOREVER);
#else
level = rt_hw_interrupt_disable();
#endif
strcpy(&name[4],self->name);
update = rt_event_create(name, RT_IPC_FLAG_FIFO); //创建跟新事件主题
*event = (uint32_t)update; //将当前跟新事件句柄返回应用层
new_subscriber = (struct orb_subscriber*)rt_malloc(sizeof(struct orb_subscriber));
strcpy(new_subscriber->subscriber,self->name);
new_subscriber->update = update; //记录主题
rt_list_insert_after(&node->sub_list,&new_subscriber->subscriber) //将主题加入到节点中
node->subscriber_num++;
#ifdef UORB_USING_MUTEX
rt_mutex_release(node->node_lock);
#else
rt_hw_interrupt_enable(level);
#endif
return RT_EOK;
}
/* 从节点处自动订阅主题 */
rt_err_t orb_subscribe_auto(orb_node *node,uint32_t *event,uint32_t time_out)
{
while (time_out--)
{
if (orb_subscribe(node, event) == RT_ERROR)
{
rt_thread_delay(RT_TICK_PER_SECOND/5);
}
}
return RT_ERROR;
}
/* 从节点处自动取消订阅主题 */
rt_err_t orb_unsubscribe_auto(orb_node *node,uint32_t *event,uint32_t time_out)
{
while (time_out--)
{
if (orb_unsubscribe(node, event) == RT_ERROR)
{
rt_thread_delay(RT_TICK_PER_SECOND / 5);
}
}
return RT_ERROR;
}
/* 从节点处取消订阅主题 */
rt_err_t orb_unsubscribe(orb_node *node,uint32_t *event)
{
rt_uint32_t level;
struct orb_subscriber *ptr = RT_NULL;
struct rt_list_node *list_node;
#ifdef UORB_USING_MUTEX
rt_mutex_take(node->node_lock, RT_WAITING_FOREVER);
#else
level = rt_hw_interrupt_disable();
#endif
if(!rt_list_isempty(&node->sub_list)) //判断节点中是否有主题订阅
{
for(list_node = node->sub_list.next;list_node != &(node->sub_list); list_node = list_node->next)
{
ptr = rt_list_entry(list_node, struct orb_subscriber, subscriber); //查找比对主题
if ((ptr->update == (void *)*event) == 0)
{
rt_list_remove(&ptr->subscriber); //删除主题订阅
rt_event_delete((rt_event_t)*event);
rt_free(ptr);
node->subscriber_num--;
}
}
}
#ifdef UORB_USING_MUTEX
rt_mutex_release(node->node_lock);
#else
rt_hw_interrupt_enable(level);
#endif
return RT_EOK;
}
/* 向节点发布主题 */
rt_err_t orb_publish(orb_node *node,void *structure)
{
rt_err_t err = RT_ERROR;
rt_err_t err_tmp;
struct orb_subscriber *ptr;
rt_uint32_t level;
rt_uint32_t subscriber_published_cnt = 0;
struct rt_list_node *list_node;
#ifdef UORB_USING_MUTEX
rt_mutex_take(node->node_lock, RT_WAITING_FOREVER);
#else
level = rt_hw_interrupt_disable();
#endif
rt_memcpy(node->orb_data,structure,node->size);
for(list_node = node->sub_list.next;list_node != &(node->sub_list); list_node = list_node->next)
{
ptr = rt_list_entry(list_node, struct orb_subscriber, subscriber);
rt_event_send((ptr->update),EVENT_ORB_DATA_UPDATE); //向线程发送跟新事件
}
#ifdef UORB_USING_MUTEX
rt_mutex_release(node->node_lock);
#else
rt_hw_interrupt_enable(level);
#endif
return err;
}
/* 节点校验是否有跟新事件*/
rt_err_t orb_check(uint32_t *update,int32_t timeout)
{
rt_uint32_t e;
if(*update != RT_NULL)
return rt_event_recv((struct rt_event*)*update, EVENT_ORB_DATA_UPDATE,
RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR ,
timeout, &e);
else
return -RT_ERROR;
}
/* 产生跟新事件,则拷贝跟新数据到线程 */
rt_err_t orb_copy(orb_node *node,void *buffer)
{
rt_uint32_t level;
#ifdef UORB_USING_MUTEX
rt_mutex_take(node->node_lock, RT_WAITING_FOREVER);
#else
level = rt_hw_interrupt_disable();
#endif
rt_memcpy(buffer,node->orb_data,node->size);
#ifdef UORB_USING_MUTEX
rt_mutex_release(node->node_lock);
#else
rt_hw_interrupt_enable(level);
#endif
return RT_EOK;
}
uORB在rtthread中开发应用
ORB_DEFINE(mavlink_formation_read_file_info, mavlink_formation_request_info_t);
orb_advertise(ORB_ID(mavlink_formation_read_file_info));
void app_formation_point_main(void *param)
{
orb_publish(ORB_ID(mavlink_formation_read_file_info), &mavlink_formation_read_file_info);
orb_publish(ORB_ID(mavlink_formation_read_mav_info), &formation_mav_file);
}
void app_log_main(void * param)
{
uint32_t mavlink_formation_read_file_info_sub = 0;
if(orb_subscribe_auto(ORB_ID(mavlink_formation_read_file_info), &mavlink_formation_read_file_info_sub, 10) == RT_ERROR)
{
rt_kprintf("[LOG]can not subscribe ORB_ID(mavlink_formation_read_file_info)\n");
}
while(run_log)
{
if (orb_check(&mavlink_formation_read_file_info_sub, 0) == RT_EOK)
{
orb_copy(ORB_ID(mavlink_formation_read_file_info), &sub.mav_formation_read_file_info);
}
rt_thread_delay(RT_TICK_PER_SECOND / 1000);
}
}