4.3.1 通讯功能
    凡是设计通讯方面的软件,首先是定义协议,以及协议所承载的内容!

通讯协议及命令定义:
基本通讯格式:(和圆梦小车及无线接口兼容)
标准UART格式  ——  19200  8  N  1
帧格式:
帧头(2字节) 接收方地址(1字节) 发送方地址(1字节) 帧长(1字节)
命令(1字节) 数据域(N字节) 校验和(1字节)
其中:
帧头 —— 由2个特殊的字节 0x55 0xAA构成;
接收方地址 —— 通讯对象的“名字”,在有线通讯时也许多余,但无线时就需要了。
发送方地址 —— 告诉接收方,便于接收方回答。
帧长 —— 命令和数据域字节之和,
命令 —— 说明操作内容,详见下面的定义
数据域 —— 与命令配合,表达一个完整的含义。
校验和 —— 从命令开始到数据域结束所有字节的算术和,取最低字节的反码。

命令定义
为了便于调试,保留小车中设计的读写内存命令。
 命令一 :读内存,实现读指定地址开始的 N 个字节,地址用两字节表示。
命令字  —— 0x01
数据域  —— 低地址(1字节) 高地址(1字节) 读字节数(1字节)
地址与硬件的对应关系:
0x0000 — 0x00FF —— 对应STC12LE4052的256字节内部RAM(idata);
0x0100 — 0x7FFF —— 保留,为大RAM的单片机预留;
0x7F80 — 0x7FFF —— 对应STC12LE2052的128字节SFR;
0x8000 — 0x87FF —— 对应STC12LE2052的2K FlashROM(Code);
0x8700 — 0xFFFF —— 保留,为大ROM的单片机预留;
例:要读地址 0x56起始的3字节内部RAM数据,命令帧如下:
0x55  0xAA  XX   XX  0x04  0x01  0x56  0x00  0x03  0xA5
返回数据帧为:
帧头  发送方地址 自己的地址  帧长  命令  低地址 高地址 读字节数 
N字节数据 校验和
返回帧中将命令及附属信息(地址、读字节数 )包含在内,虽然有些冗余,但保证了信息的完备性,不需要接收时还要查找原来读的是什么?为通讯需求日渐复杂提供方便。
 命令二:写内存,实现写指定地址开始的 N 个字节,地址用两字节表示。
命令字  —— 0x02
数据域  —— 低地址(1字节) 高地址(1字节) 写字节数(1字节)数据(N字节)
其地址与硬件的关系与读命令相同。
返回数据帧为:
帧头  发送方地址 自己的地址  帧长  命令  低地址 高地址 写成功字节数 校验和
通过“写成功字节数”来告之发送方是否写成功,如果为“0”,表示写操作失败。
 命令三:设置工作模式,设置测量模式,并启动测量。
命令字  —— 0x03
数据域  —— 工作模式(1字节) 工作参数(1字节)
其中:
工作模式 —— 高4位为主模式,低4位为子模式;
工作参数 —— 与工作模式对应,自动测量时为测量周期,单位10ms;单轮测量时为测量的次数,暂定最多为8次。

    返回数据帧为:
    帧头  发送方地址 自己的地址  帧长  命令  测量数据 校验和
    对于自动模式,测量数据返回一个字节“0”,说明OK。
    对于单轮测量模式,测量数据为工作模式中所要求的数据。
    自动模式对应的子模式:
    a) 自动数据返回 —— 每测完一个数据都返回,命令位置返回工作模式;
    b) 被动数据返回 —— 内部只是测量、保存数据,等待读数据命令读回,读数据命令实际上只对自动模式的被动数据返回有效。
单轮测量对应的子模式:
    a) 无数据处理返回 —— 只是将命令中要求测量的数据全部返回
    b) 剔除最大最小值返回 —— 将测量数据中最大、最小值剔除,返回数据比指定数少2个,当指定的测量次数小于 3 时,强制切换到“无数据处理模式”;
    c) 平均值返回 —— 将指定的测量数据平均后返回,只有一个平均值;
    d) 剔除最大最小后平均值返回 —— 先剔除最大最小值,剩下的再取平均值,当数据数小于 4时,强制切换到“剔除最大最小值” 模式。
上述返回帧中命令位置返回数据处理方式。

 命令四:读测量数据,对于自动测量方式的被动数据返回模式,需要此命令。
命令字  —— 0x04
数据域  —— 读最近几次的数据(1字节)
返回数据帧为:
帧头  发送方地址 自己的地址  帧长  命令  测量数据 校验和

通讯是个过程,包含等待处理,所以需要定义状态变量,以控制处理的内容和方式。
通讯分为两个状态:一是什么也没有收到;二是收到了帧头,等待帧结束。因此可以用位标志 g_bStartRcv 来表示,为“假”表示没有收到帧头,为“真”表示收到帧头,正在收剩余的内容。
通讯部分实现以下功能:
1) 接收数据
2) 判断帧格式
3) 解析命令,执行相应操作
4) 返回数据

根据上述功能可以看出,首先要为接收设置变量:

为了判断帧格式,解析命令,必须有相应的取数变量:

为了返回数据,需要设置发送用变量:

 
上述变量所需的常数定义:(注意缓冲区大小)


 

变量初始化:


 
因为涉及硬件,所以还要初始化硬件:


 

 
    注意:上面所有涉及寄存器初始化的参数全部用的是符号常量,这样看似麻烦,但大大改善了程序的可读性,同时也增加了可靠性;因为定义这些符号常量时不需要考虑逻辑,只需对照手册认真键入即可;而编程时只需引用那些直观的名字,不必翻阅手册。
    往往是一边思考编程逻辑一边查手册时出错,因为要一心二用!

    程序中的常数定义:(关于MCU寄存器的太多,就省略了,请看源代码)


 

    上面算是把通讯的准备工作完成了,接下来构建通讯的处理程序。
    通讯采用中断方式接收、发送数据,在中断中只做必须的事,如:将收到的数据保存到接收缓冲区、建立标志、将发送缓冲区的数据送出、送完后建立标志。
主循环中处理如下:


 
中断处理如下:


 
注意程序中的“环形”处理方式!
    接着是编写接收帧判断程序,在处理时借用存、取数指针来判断收到了几个字节,所以收到数据的标志可简化为位标志 g_bNewData。


 
具体的帧头检测、帧尾处理看程序。
    收到正确帧后,要解析命令,并做出相应的操作。通常采用switch() case() 方式处理(此处只是显示框架,详细的处理请看源代码。注意左侧的行号,使用编辑器的折叠功能是不是感觉不错?):


 
    通讯部分软件描述基本结束,读者可对照源代码消化。
如此“啰嗦”的描述不知能否将前述抽象的“思考过程”具体化,便于初学者理解。