编辑语:
芯片开放社区(OCC)面向广大开发者推出应用实战系列内容,通过分享开发者实战开发案例,总结应用开发经验,梳理开发中的常见问题及解决方案,为后续参与的开发者提供更多参考与借鉴。
关于YoC系统,我们已经介绍了其KV组件的使用,并成功移植到了RVB2601开发板。本期内容,我们将继续分享YoC的实战应用,带大家了解如何通过使用其AOS的事件标志组,避免定时器回调和w800接收回调相互阻塞的情形。
01 AOS事件标志组
停滞了一段时间,继续RVB2601创意应用的开发工作。本人的创意项目需要RVB2601连接TCP Server——这个Server是建立在LoRa网关设备上的,所以基本就是用RVB2601去做LoRa网关的无线式上位机。
工作流程大体是:RVB2601向网关发送轮询帧,网关转发给LoRa终端,终端则采集传感器数据并通过网关回传到RVB2601。因此,RVB2601既要WiFi联网,又要开启TCP应用,而且还需要接收TCP数据。
好在板子的SDK中有接收回调的注册接口,就是之前帖子已经提到过的w800_packet_input_cb_register(),回调的原型为:
typedef void (*net_data_input_cb_t)(int linkid, void *data, size_t len, char remote_ip[16], uint16_t remote_ports);
不过案例的业务逻辑设计了这样一种需求:板子每隔固定周期会发出轮询帧,且发出后等待回复数据一定时间(默认10s),如果超时则再发轮询帧,最多尝试3次(也是默认,可以调整),这个需求通过定时器回调来实现的。好巧不巧上一阶段的工作主要完成NTP功能,自以为对C time库了解的不错,所以本人设计这样“有坑版”的代码写法:
//w800 input callback function void aita_w800in_cb(int linkid, void *data, size_t len, char remote_ip[16], uint16_t remote_ports) { uint8_t* d; d = (uint8_t *)data; printf("linkid: %d\n", linkid); printf("len: %d\n", len); printf("remote_ip: %s\n", remote_ip); printf("remote_port: %d\n", remote_ports); printf("data:%s\n", d); if(remote_ports == GW_PORT) { isTimeout = 0; memset(USR220resbuff, 0, USR220RES_LEN); for(int i=0; i<len; i++) { USR220resbuff[i] = d[i]; } } } void aita_PollingFunc(void) { int i = 0; int cyc = 1; time_t nowtime, endtime; //connect to USR220 TCP Server int ret = -1; ret = w800_connect_remote(0, NET_TYPE_TCP_CLIENT, GW_IP, GW_PORT); if(ret < 0) return; for(i=0; i<endian_count; i++) { aita_BuildCommandPack(i); //build polling pack cyc = repeat; //repeat times when timeout isOneEndianDone = 0; while(cyc--) { w800_send_data(comm_pack, COMMPACK_LEN, 3000); isTimeout = 1; endtime = time(NULL) + timeout; while((nowtime=time(NULL)) < endtime) { if(!isTimeout) { cyc = 0; isOneEndianDone = 1; break; } } printf("usr220res: %s, len: %d\n", USR220resbuff, sizeof(USR220resbuff)); } if(!isOneEndianDone) { printf("%s disconnected\n", EID[i]); } } //断开TCP 连接 w800_close(0); }
上述代码的逻辑也很简单,就是基于程序查询思想,发出一帧开始不断通过time(NULL)来获取本地时间,如果时间过去timeout秒,就表示超时了,而w800接收回调会将超时标志isTimeOut清零,帮助结束程序查询的循环——while((nowtime=time(NULL)) < endtime)。
逻辑看似完美,但是在下忽略了这是在实时系统的平台上,而且代码跑在定时器回调中,而网络接收回调抢不到CPU权,所以查询本地时间的循环会阻塞w800接收回调的出现。
当时,为了验证自己的想法,干脆将超时查询改为aos_msleep(timeout*1000),果然接收回调出现了,但是每一次轮询都要固定耗时timeout秒钟显然是不行的。因此,想到了引入事件机制,因为只是简单的条件获取,所以选用了事件标志组。YoC的事件标志组可以查看链接:
首先定义事件标志组变量和初始化函数,初始化函数会在案例的board_yoc_init()中调用。下述代码中,标志定义为宏EVENT_FLAG_3,选用这个纯粹是从文档抄来也没必要改名字。
#define EVENT_FLAG_3 0x000000F0 aos_event_t usr220res_event; void aita_InitEvent(void) { int ret = -1; ret = aos_event_new(&usr220res_event, 0); if (ret != 0) { printf("event create failed\n"); return; } }
接着就是修改aita_PollingFunc()函数,它会在定时器回调中调用。
void aita_PollingFunc(void) { int i = 0; int cyc = 1; //connect to USR220(LoRa Gateway) TCP Server int ret = -1; ret = w800_connect_remote(0, NET_TYPE_TCP_CLIENT, GW_IP, GW_PORT); if(ret < 0) return; //once polling request all endians for(i=0; i<endian_count; i++) { aita_BuildCommandPack(i);//build req pack in USR220 format cyc = repeat; isOneEndianDone = 0; while(cyc--) { w800_send_data(comm_pack, COMMPACK_LEN, 3000); isTimeout = 1; uint32_t actl_flags; //wait for flag(0x000000F0) for timeout-seconds, if get the flag then clear the bit aos_event_get(&usr220res_event, EVENT_FLAG_3, AOS_EVENT_OR_CLEAR, &actl_flags, timeout*1000); if(!isTimeout) { cyc = 0; isOneEndianDone = 1; printf("usr220res: %s, len: %d\n", USR220resbuff, sizeof(USR220resbuff)); } } if(!isOneEndianDone) { printf("%s disconnected\n", EID[i]); } } //once polling done & disconnect to tcp server w800_close(0); }
w800接收回调当然就是在收到回复数据包后,发出事件标志EVENT_FLAG_3。
void aita_w800in_cb(int linkid, void *data, size_t len, char remote_ip[16], uint16_t remote_ports) { uint8_t* d; d = (uint8_t *)data; printf("linkid: %d\n", linkid); printf("len: %d\n", len); printf("remote_ip: %s\n", remote_ip); printf("remote_port: %d\n", remote_ports); printf("data:%s\n", d); if(remote_ports == GW_PORT) { isTimeout = 0; memset(USR220resbuff, 0, USR220RES_LEN); for(int i=0; i<len; i++) { USR220resbuff[i] = d[i]; } aos_event_set(&usr220res_event, EVENT_FLAG_3, AOS_EVENT_OR); } }