OSI 模型这个话题,虽然在教材和网络上已经被讲了无数次,但大多数讲解都以计算机网络为背景,从 HTTP、TCP 等协议展开说明。
说实话作为既非计算机科班出身,也非网络相关行业从业者的我来说,计算机网络是十分陌生且晦涩的,对初学者不太友好。当初我看完计算机网络书籍之后,仍云里雾里的,还是不太理解分层的真正意义和它跟实际开发有什么关系。
经过几年工作实践,动手写协议、调通信、分析数据包,才逐渐理解每一层的价值,所以今天想尝试抛开传统的网络角度,站在“嵌入式通信协议”的视角,重新解释一下 OSI 模型。
本文内容仅为个人工作实践总结,能力有限,如有谬误,欢迎指正,谢谢。
OSI 模型(Open Systems Interconnection Model,开放式系统互联模型)是一个用于描述网络通信过程的分层模型,共分为了七层,每层有各自的清晰的职责分工,十分便于系统设计和协议开发。
受限于资源,在嵌入式中通常不会完整实现 OSI 七层,但该模型对于通信协议的设计和开发非常有指导意义,在实际项目中会根据系统需求简化为3~5层通信协议结构。
完整的模型分层如下图(图片来自《嵌入式系统:硬件、软件及软硬件协同》,作者Tammy Noergaard):
下层对于上层都是透明的,即在逻辑上两个设备上对应层之间是可以直接交互的。
第1层:物理层
该层提供电气和物理规格
关注内容:
- 通信需要通过几根线相连?
- 工作电压多少?多少电压代表“1”,多少电压代表“0”
- 通信速率是多少?
- 数据支持的传输方向?单工、半双工、双工?
- 设备连接方式?点对点、一主多从、多主多从?
UART示例:
- 需要Vcc、Tx、Rx、GND四根线;
- 电平标准是RS232或者TTL;
- 通信速率不定,设备双方约定好即可,常见的速率有115200bps、9600bps等;
- 支持全双工,即可同时收发数据;
- 设备连接是点对点;
第2层:数据链路层
描述字节如何在物理线缆上传输。
关注内容:
- 在多设备场景中,当前的数据是要发给谁的?
- 数据帧的bit如何定义?帧头、帧尾、校验码?
- 数据帧有无校验?什么校验方式?
- 每帧数据的数据量是多少?
- 数据的传输流向如何规定?
UART示例:
- 由于UART设备点对点连接,第一个问题无需考虑,而在支持多设备的协议中,如SPI使用CS进行片选、IIC使用设备地址进行选择;
- UART数据定义一帧数据有起始位(帧头)+8位数据位+校验位(可选)+停止位(帧尾);
- UART可选使用奇偶校验,不是所有协议都在该层有校验,如SPI;
- UART每帧10~11.5bits;
- UART数据全双工,也无需考虑,如半双工的IIC则是通过ACK来交换数据线的控制权的;
第3层:网络层
该层负责将一个可变长的信息(包)从一端传送到另一端。
关注内容:
- 当前信息是要发给谁的?和上层第一个问题不同的是,这里表述的是软件层面上的地址,而不是物理地址
- 数据帧要如何分包组包?
软件地址用来指向设备中的抽象主体,这在嵌入式中比较少见,一般就一个主体去处理通信信息。举个比较相似的例子,一般使用IIC去读写外部寄存器时:起始->从设备地址(读/写)->寄存器地址->ACK/NACK;其中的寄存器地址就如同软件地址;
UART一帧数据有效位只有8位,显然不能满足大部分应用需求,所以需要接收/发送多帧,最后将多帧数据组合成完整的数据包。这个时候就需要规定哪些帧是属于同一包的:
- 定长,规定固定8帧或者16帧为一包?
- 超时,规定多久没有数据流那前面的数据为一包?
- 魔数,特定包头包尾来进行划分?
AT指令示例:AT+ID?
一帧数据发送一个ASCII码,该指令可以分解为 "A" "T" "+" "I" "D" "?" "\r" "\n"共8帧
"AT"可作包头,"\r\n"可作包尾。
第4层:传输层
该层提供可靠的机制来传输数据。
关注内容:
基于传输效率考虑,数据链路层不会提供过于复杂的校验,甚至是无校验。那为了保证整包数据的准确性,增加校验是必要的,常见的校验方式有CheckSum校验、CRC校验;
那校验后发现个别数据帧错漏要请求重传呢?比如,如果有包序信息,那就可以单独请求该包序号的数据重新发送。
第5层:会话层
负责建立、管理和终止会话。
关注内容:
我觉得可以把会话换成交互来理解,一次会话就是两个设备之间一次完整的通信交互。
如AT指令交互:
主设备发送: AT+BATTERY_VOLT?
从设备回复: +BATTERY_VOLT: 3750,40
从设备确认结束: OK
以上就是一次会话,从AT+CMD?开始建立会话,到+CMD:value进行会话交互,最后到OK终止会话。
第6层:表示层
该层定义数据结构,可能还会有压缩和加密。
关注内容:
- 定义数据结构,转换 row data 成结构体
- 可能包含加密、压缩等(高级应用)
以上面的AT指令为例:
接收到的row_data[LEN] = {0xA6,0x0E,0x28};
数据定义结构定义struct {uint16_t volt;uint8_t percent};
经过表示层处理后即可输出volt = 3750;percent = 40;
第7层:应用层
该层接受用户交互并形成一个通信请求。
关注内容:
- 提供接口供业务模块调用,发起通信请求
- 屏蔽底层通信细节
即提供一个调用接口给其他模块,用来发起通信。
如:send_cmd_to_slave(uint8_t cmd,uint8_t *param);
那当其他模块,如按键模块,识别到Key1被按下时就查询从设备电量,可以调用send_cmd_to_slave(CMD_REQUEST_BAT_PERCENT, NULL);发起通信查询。
这样,其它模块就不需要关心底层用的什么协议,什么接口,内部具体什么交互逻辑。
嵌入式常用通信结构
在资源极其有限的mcu中,一般会简化到3层:
- 物理层:收发数据,例如 UART/SPI/I2C 驱动;
- 数据链路层:处理帧结构,分帧/组帧,处理包校验;
- 应用层:处理数据业务逻辑,如 AT 指令解析;
当项目稍微复杂后,推荐使用 5 层:
- 物理层:收发数据,例如 UART/SPI/I2C 驱动;
- 链路层:处理帧结构,分帧/组帧,处理包校验;
- 网络层:软件地址识别,拆包组包;
- 表示层:结构化数据,映射为具体含义的变量;
- 应用层:负责通信业务逻辑,如命令处理、回复响应。
小结
OSI 模型虽然来源于计算机网络,但其“分层设计”的理念在嵌入式通信中同样适用。
其实对于嵌入式开发者来说,学习OSI 模型最重要的并不是去死记硬背每一层的具体定义,而是要理解“为什么要分层”、“每层解决什么问题”。清晰的职责划分才能带来更好的可维护性、可扩展性。
来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除 |