首页 > 编程知识 正文

win32消息驱动,驱动系统和响应系统

时间:2023-05-04 21:19:48 阅读:135637 作者:4528

读了昨天发送的博客的朋友应该知道一些接收模块响应的方法。 如果你没有看到,请点击分享几种单片机接收AT命令响应数据的方法。

前言今天我们分享一种从单片机中分离URC数据的方法。 与上述胡说八道不同,这次是全篇晾衣架哦。

接触到报道的表驱动法可以说是这次的精髓。 这里不怎么说明表驱动法。 感兴趣的人可以自己查相关资料。 我这里只是用它来实现功能。 这个设计方法真的很香。

在表驱动方法中,也有URC间的包粘贴处理、命令回复和URC间的包粘贴处理。

你为什么要做这个? 主要原因是之前刚毕业的时候,买了一块淘宝板,发现他们的代码都是死的。 也就是说,将AT发送到模块,模块本身返回OK。 但是,他们的实现方法不等待OK,等某个URC到达后,再进行下一步。 说实话,太打捞了,没有效果。

AT )主机发送(OK )命令回复(cmd1:xx ) URC1 ) cmd2:xx ) URC2 ) . cmdn:xx ) URCn )在发送AT后等待某个URCn而不是OK 这样,经过调试后,可以切实达到目标效果。 这是

分离各部分后,可以有效管理模块的执行状态。 例如,在测试时,需要长期测试模块的稳定性。 那么,发送AT后,虽然得不到URCn,但得到了OK。 当然,我不认为这个AT步骤有问题。 另外,这样的话很难定位为问题。 中间的URCx在哪里?

URC接下来进入正题。 首先,什么是URC?

咳,百度找不到对应的词条,我来翻译吧

urc : unsolicited result代码可以翻译为未请求的结果代码或未指示的结果代码。

这是什么意思呢,发送AT并返回OK的q&; 与a模式不同,URC是主机,即单片机没有向模块发送命令,但模块主动向主机发送具有特定意义的命令的形式。

由于通信模块的不同协议具有不同的复杂性,因此制造商封装的AT命令各不相同。 登录云平台时,模块会通过URC通知主机当前在哪个步骤运行,网络状态发生变化时,模块会主动向主机通知命令,从平台发出数据,或者移动可以说除了AT命令的答疑之外,剩下的消息都是通过URC进行的。

不,看了这么多URC消息,各自的意思还不一样,该怎么办? 不要着急,一步一步地分析吧。

首先,要确定URC消息格式,典型的通信模块URC格式如下:

CMD1: param1、param2rn CMD2: param1、param2rn .是的,这看起来很简单。 但是,我在程序中怎么处理这些数据呢? 细心的朋友已经发现URC消息以+XXX开头。 那么,找到这个+XXX的话不就找到这个数据了吗? 没错! 找到这个头后成功了一半。 这也是今天要讲的重点。表驱动法实现解析URC消息

什么是表驱动法,什么是表驱动法? 表驱动法(Table-Driven Approach )简单来说就是用样本法获取数据。 这里的“表”通常是数组,但可以看作是数据库的一种表示。 (抄写的)

其实我们都用过表驱动法的实例。 列举每个人都用过的东西,是字典。 从词典部首表中检索字的拼音未知汉字是根据某个字的字形计算索引值,并映射到相应的页码的常用表驱动方法。 与一页一页地翻词典相比,部首检字法效率非常高。

现实生活中有很多例子,包括超市货架的分类、蔬菜市场的划分、包括键盘在内的钟表驱动法的样子等。

与编程方相对应,在数据较少时可以使用(if…else )或(switch…case ),但数据量开始增多时,逻辑判断语句会增加,代码会变得越来越复杂。 此时,表驱动法的优点就会显现出来。

表格驱动法本身是通过很多方式实现的,但是这里博客的经验有限,所以没有专门阅读过相关书籍。 数据结构和算法是用自己常用的方案制作的,有错误的地方请多关照。

实现方案以下为在EC616系列模块上连接CTWing的示例。 EC616在对接CTWing平台时需要处理一些必需的URC消息。 分别如下

CTM2MRECV: len,datarn CTM2M: operation,status code .rNCT m2m cmd : msgid,cmdtype,token,uri _ri

此时,根据上面的说法,我只需要找到前面的几个URC头部就可以了。 是的,这是今天的任务。 但是,在第二个消息主体中操作参数是否为1

个定值,具有多个选择的一个参数,参数可取值如下:

reg obsrv update ping dereg send lwstatus

  也就是说,第二条URC实际上又可以分成7条不同的URC,虽然,我也可以把第二条消息分解成7份,只建一个表格,进行维护,但是为了形象的表示,主体URC只有3条,我又多建了一个表格,单独维护 +CTM2M: 的<operation>字段。
  分析完本次要解析的URC之后,下面就要开始对表格的数据结构进行规划,因为URC处理只需要查找对应的索引,所以,数据结构的建立也很简单,我这里以此方式建立结构体:

typedef struct{ char *cmd; void (*CmdCallback)(void);}Type_CmdCallback;

  数据结构建立的非常简单,一个关键字加上对应的回调函数即可。当然也可以将此数据结构再进行优化,例如:

typedef struct{ char *cmd; void (*CmdCallback)(char *urcbuf, uint32_t urc_len, void *arg);}Type_CmdCallback;

  若以第二种方式建表的话,不需要维护特意urc全局数组,在判断是谁索引后,直接将当前的urc数据传入回调函数中即可,并且预留一个空指针,以供用户使用。
  为了简单说明,我这里以第一种方式建立维护整体URC的表格:

Type_CmdCallback urc_cmd_table[] = { {"+CTM2MRECV:", ctm2mrecv_handle }, {"+CTM2M:", ctm2m_handle }, {"+CTM2MCMD:", ctm2mcmd_handle },};

  至于 +CTM2M: 的分支表格,这里不作啰嗦,直接给出相应的表格:

Type_Ctm2mCallback urc_ctm2m_table[] = { {"reg", ctm2m_reg_handle }, {"obsrv", ctm2m_obsrv_handle }, {"update", ctm2m_update_handle }, {"ping", ctm2m_ping_handle }, {"dereg", ctm2m_dereg_handle }, {"send", ctm2m_send_handle }, {"lwstatus", ctm2m_lwstatus_handle },};

  在 +CTM2M: 的回调函数中,按照同样的方法判断urcbuf存在的分支索引,继续再进行数据处理就可以了。
  根据上次我们学习的如何接收模组响应的一整包数据,在串口认为接收完成以后,开始进行判断,当前的响应是AT指令的期望返回还是错误代码,如果都不是,则认为此次是URC数据,就将urc_flag置位,并将当前数据包复制到urcbuf中去。在urc_loop函数中,不停的判断urc_flag是否被置位,若被置位就开始查找,表格中的cmd字段是否在urcbuf中,若在,则根据不同的索引,进入注册的回调函数。(说的有点乱)
  ~ps: strstr是查找b字符串在a字符串中第一次出现的位置.~

void urc_loop(void ){ uint8_t _cmd_index = 0 ; if(scp5_info.urc_flag == 1) { while(strstr((const char *)scp5_info.urc_rxbuf, urc_cmd_table[_cmd_index].cmd) == NULL) { _cmd_index++; if(_cmd_index >= 3) goto next; } urc_cmd_table[_cmd_index].CmdCallback(); scp5_urc_clear(); }next: ;}

  以上就是在urcbuf中查找所注册过的urc头部的办法,当然,这个办法有局限性,比如在使用超时中断来判断接收完成时,可能会将两条消息当成一条消息,这样找到的只有在表格中注册的靠前的那一条urc,靠后的一条就无法找到了,这就是后面要提到的urc与urc之间的粘包拆包问题。
  上述现象如下:

+CTM2M: XXX +CTM2MCMD: XXX

  若遇到此现象,则上述办法无法生效,只能检测到表格中的第一条消息,第二条消息无法感知,此时,则需要另寻办法对数据包再次解析,以确保,消息不会丢掉。
  因为一般的粘包最多会在两条urc之间产生,所以暂时不考虑三个以上的场景,我的做法如下:

void urc_loop(void ){ int _cmd_index = 0 ; if(scp5_info.urc_flag == 1) { while(strstr((const char *)scp5_info.urc_rxbuf, urc_cmd_table[_cmd_index].cmd) == NULL) { _cmd_index++; if(_cmd_index >= 3) goto next; } urc_cmd_table[_cmd_index].CmdCallback(); if(++_cmd_index > 3) goto clear; while(strstr((const char *)urc_buf, urc_cmd_table[_cmd_index].cmd) == NULL) { _cmd_index++; if(_cmd_index >= 3) goto clear; } urc_cmd_table[_cmd_index].CmdCallback();clear: scp5_urc_clear(); }next: ;}

  处理方法,很简单,只是做了重复的工作,但是当初实现时,却是在++_cmd_index上花费了不少功夫(大坑),不过,如果你使用的是前文中的 串口中断+rn 形式,则不会出现这个问题,这就是需要取舍的地方。
  对于AT指令响应和URC之间的粘包问题,方法和此处类似,只是在判断完AT响应后,需要再次判断数据包中是否含有URC部分,再进行处理。这里不再详细说明。
  从这里就可以看出,表驱动法的好处:

1、提高程序的可读性。一个消息如何处理,只要看一下驱动表就知道,非常明显。
2、减少了重复代码。程序不再含有超级多的if…else…。
3、可扩展性。若要新增某一类似功能,只需要在urc表中将其头部注册上去并完善回调函数即可。
4、降低了复杂度。

  说是全篇干货,结果到最后又是废话连篇,希望以后在文章写得多的时候,会有些进步吧!

  如有问题,欢迎在下方评论留言。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。