最近一直在忙着做NBlot模块,话说博客也好久没有更新了,最近整理下思路,
做下总结,权当是一片博客吧。
AT指令大家很熟悉了,这里不再做介绍(不熟悉的,请参考《GSM Rec.07.07标准AT命令》)。
一个命令类型最多分为四类:
1、设置类型,CMD=设置参数,回复OK或者ERROR; 可用通用模式解析
2、读取类型,CMD?读取参数当前值,回复+CMD:参数列表;解析返回字符串,可能做命令转变
3、测试类型,CMD=?读取默认参数值和可设置范围; 解析返回字符串,可能做命令转变,也有可能返回OK
4、执行类型,CMD 读取不可设置参数值; 解析返回字符串,可能做命令转变
这里解析主要运用到消息地图的方法,同时为了减小地图的大小,每个命令字节不具体细分是那个
命令类别,交给具体的命令解析函数去处理。如下:
//! 消息地图
const static msg_t c_tMSGMap[] = {
{"AT", JZQ_AT_CMD_AT},
{"ATE0", JZQ_AT_CMD_ATE0},
{"AT$MYGMR", JZQ_AT_CMD_MYGMR},
{"AT$MYTYPE", JZQ_AT_CMD_MYTYPE},
{"ATI", JZQ_AT_CMD_ATI},
{"AT$MYCCID", JZQ_AT_CMD_MYCCID},
{"AT+CSQ", JZQ_AT_CMD_CSQ},
{"AT+CREG", JZQ_AT_CMD_CREG},
{"AT+CIMI", JZQ_AT_CMD_CIMI},
};
在具体的命令解析函数里面,都次都可以指定NBlot模组的数据返回解析函数,做到每个命令和命令
的回复处理函数一 一对应。如下:
发送给NBlot模块命令:
user_nblot_send(get_byte_pipe_buffer(&tBytePipe),get_byte_pipe_num(&tBytePipe));
s_tNblotAppBuffer.pRxCallBack = JZQ_AT_CMD_MYNETWRITE_ACK;
NBlot模块的命令接收解析:
switch(s_tState){
case FSM_NBLOT_PROTOCOL_START:
s_tState = FSM_NBLOT_PROTOCOL_WAIT_DATA;
s_tNBlotAppBuffer.hwBufferSize = 0;
//break;
case FSM_NBLOT_PROTOCOL_WAIT_DATA:
if(nblot_rx_buffer_is_empty()){
break;
}
s_tState = FSM_NBLOT_PROTOCOL_REV_FRAME;
//break;
case FSM_NBLOT_PROTOCOL_REV_FRAME:
tFsmRt = nblot_protocol_revice_frame();
if(fsm_rt_cpl == tFsmRt|| fsm_rt_buffer_full == tFsmRt){
s_tState = FSM_NBLOT_PROTOCOL_HANDLE;
}else if(fsm_rt_err == tFsmRt){
RESET_FSM_NBLOT_PROTOCOL();
return fsm_rt_err;
}
break;
case FSM_NBLOT_PROTOCOL_HANDLE:
if(NULL != s_ptRxCallBack){
s_ptRxCallBack(s_tNBlotAppBuffer.chBuffer,s_tNBlotAppBuffer.hwBufferSize);
}
RESET_FSM_NBLOT_PROTOCOL();
return fsm_rt_cpl;
default:
FSM_DEFAULT_ACTION();
}
至此一个大体的AT命令解析架构介绍完毕了,但是这个架构会有一个问题,
就是如果仅仅考虑问答式命令形式,这个架构再加一个超时无效处理的维护
线程就可以(维护线程就干一件事情,发送命令后,启动维护线程,设置超
时命令,如果在设置的超时时间内回复,则通过发送命令时候指定的处理函
数解析,如果超时了,就把指针清NULL,同时发送超时消息)。如果还要考
虑主动上报,那么上面的架构就需要在(每个)处理函数里面加入主动上报
的处理逻辑,否则逻辑上就不对。这样就会产生很多“脏代码”,处理方法是利
用行为继承,在FSM_NBLOT_PROTOCOL_HANDLE前面增加一个DEFULT_HANDLE
处理,把一些通用或者主动上报的(每个命令帧都需要考虑的情况)打包成一个处理函数。
到目前为止,AT指令解析,基本讲完了,说些题外话:
1、下行数据可能要考虑拼包问题,因为在测试过程中发现主站下发一个APDU,在模块
端会被分为两个socket包,并且时间间隔还很长(目前监控到最长30S);这里有一
个问题,如果考虑拼包,那么就和协议关联了。
2、socket会自己断开,一定要有重建机制;
2018.10.23
前几天和一位老工程师交流这个AT指令解析方法,有一些心得,记录下,也算是一种
思想上的提高。
这个解析方法有个缺点,就是当指令阅读,消息地图越大,检索时间越长。如果系统
要求这个检索时间和地图大小无关,时间是“确定”的,怎么解决?
首先我们就想到是什么?什么情况下检索时间和数据表大小无关?我们是不是想到了
数组下标,查表的方法。没错,查表的时间确定,并且和表的大小无关。那么我们怎么进行
查表呢?首先我们需要有一个格式化算法,把AT指令流格式化为数组下标,然后用数组下标
直接查表来得到解析函数。
这个格式化的算法怎么设计,我目前还没有很好的思路,如果你有什么好的想法,请留言
给我。
2018.12.26
最近有个NTC测温的项目,是另外一个同事负责,给出的温度和阻值对应曲线不是线性的。
于是这个同事就犯愁了,如果用传统的if/else和switch/case架构去做分段解析,那么精度要求高的
时候,段就会分很多;如果段分的少,精度又下降了。那有没有一种思路,既可以兼顾精度,又可以
使代码架构简单,降低体力活力度?于是有位老专家提出了一种“算法”,用折线去模拟曲线,同时
又没有if/else判断,我感觉思路很好,记录如下:
1、首先用折线去模拟曲线,这个没什么好办法,每段去选择合适的K和B的参数,这样就有一张参数表;
2、那么怎么知道现在读取的阻值是落到那一段呢?摒弃程序分支判断的方法就是查表法,就是说根据
阻值我就可以计算出目前在表里的位置,然后查表就得到了K和B;
3、这里取折线段的方法很重要,你可以用线性发(均分法),这样查表算法就是kx+b;如果采用非线性
方法,那个查表算法很可能是高次函数或者指数等高阶函数;
这里就设计到计算资源问题,当然根据精度,采用定点算法,实时性会大大提高。
2022.08.02
上面遗留了一个问题,一直没有写解决思路,现在把解决思路总结下。问题如下:
这里说如果考虑拼包,就和协议绑定了,是因为原来的解决思路是需要知道用户协议,才能去接收数据里面去找数据帧,才能够知道一个APDU是否完整。这样的思路没有问题,但是用户协议和我的底层驱动绑定了,换种协议,我就得重写驱动。下面说下后来常用的做法:
1、我们思考下那些是“变化”的?
2、怎么把变化的部分分离?
从上可以看出,用户协议是变化的,那怎么分离呢?把它打包成函数,每次只改写这个函数,再进一步,把这个函数指针当成参数,传入到底层驱动。
1、check函数指针输入到此等驱动,可以如下写法:返回0:则表示没有找到,返回非零,则表示找到数据帧,并且代表数据帧长度,下面驱动出队这么多。
2、下层驱动接收到数据,则每次重置超时时间,超时时间到则全部出队。
以上就是最近几年经常用的架构。