OpENer协议栈性能优化
在hpm_sdk中OpENer协议栈是和lwip和FreeRTOS搭配使用的,其性能受到这两个中间件配置的制约。
例如其可配置的最小的调度周期,受到FreeRTOS的系统频率制约,高于FreeRTOS系统频率的通信频率显然是无法保证的。
下面用一个例子来分析OpENer协议栈的调度过程,给大家优化OpENer性能做个参考,抛砖引玉。
首先来看一下OpENer的调度器的位置
middleware\opener\ports\generic_networkhandler.c
调度器位于ports目录下,意味着这个调度器是一个通用的调度器,同时意味着优化的时候可以尝试去修改它
OpENer的调度主要依靠NetworkHandlerProcessCyclic函数
这个函数在opener app的主要线程中被周期性调用
static void opener_thread(void const *argument)
{
...
while (!g_end_stack) {
if (kEipStatusOk != NetworkHandlerProcessCyclic()) {
OPENER_TRACE_ERR("Error in NetworkHandler loop! Exiting OpENer!\n");
g_end_stack = 1; /* end loop in case of error */
}
...
} /* loop ended */
...
}
NetworkHandlerProcessCyclic这个接口内部会在select接口处挂起,直到超时或者收到新的信息。
int ready_socket = select(highest_socket_handle + 1, &read_socket, 0, 0, &g_time_value);
select接口内部由lwip和freeRTOS实现,只要系统频率足够,我们可以不看其内部实现。专注与传入的时间参数g_time_value
g_time_value.tv_usec =
(g_network_status.elapsed_time < kOpenerTimerTickInMilliSeconds ? kOpenerTimerTickInMilliSeconds - g_network_status.elapsed_time : 0) *
1000;
上述的代码中配置了select接口的超时参数,可以看出,其超时的单位是毫秒,期望的调度周期应该是受kOpenerTimerTickInMilliSeconds变量影响。这个变量存在于用户配置头文件opener_user_conf.h中。也就是说修改kOpenerTimerTickInMilliSeconds变量的值,会影响到调度的期望调度周期。下面我们来做个对照试验。主机配置通信pri为1ms,抓包时只抓取从mcu端发送出来的CIP I/O信息
下面是kOpenerTimerTickInMilliSeconds=5ms时的通信抓包
可以看到虽然主机将pri配置成了1ms,但是受到调度器的限制,实际的包发送频率达不到1ms的要求。
这里细心的朋友可能发现了,为什么发送的频率不是5ms,而是小于5ms呢?其实这里涉及到了select接口的实现,虽然理论上调度周期是5ms,但是在收到主机新的数据包后,select接口会返回以便OpENer处理接收到的信息,此时OpENer会检查到有新的数据包需要被发送出去,所以才产生了短于5ms的发送频率。
下面时kOpenerTimerTickInMilliSeconds=1ms时的通信抓包
从上面的情况可以看出修改kOpenerTimerTickInMilliSeconds后,对OpENer发送的频率有影响。
目前sdk中的代码,最低可以调整到1ms的调度周期,如果想要低于1ms的调度周期,FreeRTOS的配置,还有lwip与FreeRTOS套阶层的部分宏,以及OpENer generic_networkhandler.c中以毫秒做单位的时间变量,都需要修改。在这里不赘述了,只要我们把原理搞清楚,配合一些调试手段,相信大家也是可以轻松完成修改的。