一、串行通信简介
定义:串行通信是一种使用串行数据流来传送数据的通信协议,相区别于“并行通信”;串行通信的特点是使用一根电线完成发送数据,同时使用另一根电线完成接受数据。
上面提到“串行通信”只是一种协议,那么对这个协议的实现分别有:RS232标准、RS485标准。这就意味着在开发这些协议对应的“上位机软件”的时候,可以同一个电脑编程“串口通信编程库”。
二、串口参数配置
-
端口 port : [com1、com2、等]
指使用哪个串行端口通信。 -
波特率 Baud Rate : [2400、4800、9600、19200 等]
这是一个衡量符号传输速率的参数。指的是信号被调制以后在单位时间内的变化,即单位时间内载波参数变化的次数,如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为240Bd,比特率为10位*240个/秒=2400bps。高波特率常常用于放置的很近的仪器间的通信 -
数据位 Data Bits : [5、6、7、8]
这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据往往不会是8位的,标准的值是6、7和8位。 -
停止位 Stop Bits : [1、1.5、2]
用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。串口通信首先是设置波特率,由此决定每位数据在线上维持的时间。
以传输1bit所需要的时间作为一个单位时间。
1位停止位表示 停止信号在线上维持一个单位时间。
2位停止位表示 停止信号在线上维持两个单位时间。 -
奇偶校验位 Parity : [Odd、Even、NONE、Mark、Space]
奇偶校验是对数据传输正确性的一种校验方法。在数据传输前附加一位奇校验位,用来表示传输的数据中"1"的个数是奇数还是偶数,为奇数时,校验位置为"0",否则置为"1",用以保持数据的奇偶性不变。
Odd:奇校验
Even:偶校验
NONE:无校验
Mark:校验位始终为1(不常用)
Space:校验位始终为0(不常用)
三、硬件相关知识
-
单工、半双工、全双工
单工数据传输只支持数据在一个方向上传输。
半双工数据传输允许数据在两个方向上传输,但是在某一时刻只允许数据在一个方向上传输。
全双工数据通信允许数据同时在两个方向上传输。 -
RS232、RS485 各自的特点
RS232:
接口的信号电平值较高
传输速率有局限(距离越远传输越慢)
传输距离有限,最多只能通信几十米。
通信的时候只能两点之间进行通信,不能够实现多机联网通信
全双工数据通信
RS485:
采用差分信号
通信速率快,最大传输速度可以达到 10Mb/s 以上
传输距离最远可以达到 1200 米左右
可以在总线上进行联网实现多机通信
半双工通信 -
下位机与上位机连线方式
方式一
方式二
方式三
方式四
四、硬件芯片实现串口编程
一般MCU自带串口功能,只需配置对应的“寄存器”或使用对应的库函数,即可实现串口功能开发。
注意:上位机和下位机所使用的串口参数要一致,否则无法完成通信。比如硬件用波特率9600,那么上位机软件也要使用波特率为9600。
五、软件实现串口通信的相关知识
在各种计算机系统平台下,以及各种上位机开发平台上都提供了串口库函数,通过调用库函数即可完成串口通信开发。
然后使用软件界面开发函数开发出“交互界面”和“数据处理逻辑”即可;
1. 阻塞模式、非阻塞模式
在开发上位串口中,在初始化串口的时候,可以选择是“阻塞模式”或“非阻塞模式”,这个模式选择只影响串口接收函数:
- 阻塞模式:调用接收函数后,接收函数阻止程序继续运行,直到收到串口数据
- 非阻塞模式:调用接收函数之后,函数判断串口接收缓存是否有数据,有则返回数据,没有则之间返回0,即,无论收到或没有收到数据,接收函数都不阻塞程序继续运行。
2. 结合各自特点,该选哪个阻塞模式?
答案是看你的需求,如果你所需功能只是,定期看一看有没有收到数据(无论收到数据或没有收到数据都有功能意义),那么根据你这个需求你应该使用“非阻塞模式”。而大多数功能需求情况是,只有收到数据的时候才有功能意义(在没有收到数据的时候,不需要做任何处理)。因此你应该使用“阻塞模式”。
- “阻塞模式”单线程编程示例(使用伪代码):
//使用C编程的伪代码
int main() {
inti_uart();//初始化串口
send("hello hardware!");
delay(100);
while(true) {
char buffer[10]; //缓存
receive(buffer, 10);//阻塞式读取串口数据
process(buffer, 10);//处理收到的数据。只要收到数据后,才执行次行以及其后代码
}
return 0;
}
- “阻塞模式”多线程编程示例(使用伪代码):
//使用C++编程的伪代码
static void thread_function() //子线程函数,在这个线程中专门循环接收,并处理数据
{
while(true) {
char buffer[10]; //缓存
receive(buffer, 10);//阻塞式读取串口数据
process(buffer, 10);//处理收到的数据。只要收到数据后,才执行次行以及其后代码
}
}
int main() {
inti_uart();
start_thread(thread_function);//开启线程
while(ture) { //主线程:一直给硬件发送“hello hardware!”
send("hello hardware!");
delay(100);
}
return 0;
}
3. 上位机实现查找串口设备的原理
-
在上位开发中实现连接下位机设备的时候,经常需要“查找设备”的功能。以方便用户连接下位机设备。
那么在使用串口通信的时候怎么实现呢?
在开发平台上一般会有提供这个“串口设备列举函数”,调用这个系统函数会返回可用的串口设备号。这是完成了查找设备的基础。使用这个功能可以给用户返回一个设备列表,然后让用户选择连接哪个。 -
如果更进一步,要实现查找设备并自动连接呢?
由于在查找出来的多个串口设备中只有一个才是真正的你要连的设备,怎样区别出这个设备呢,这就需要上位机和下位机之间建立一套“自家设备识别协议”。简单实现如下:
- 握手协议(相当于上位机与下位机自定义私有协议[API]):
上位机给下位机通过串口发送“who are you?(可以是任意自定义字符串)”
自家下位机收到这个字符串后给上位机发送“I’m your budy!(可以是任意自定义字符串)” - 握手协议使用方法:
上位机软件从查找到的串口设备列表中选一个设备,然后打开设备并按照“握手协议”发送数据,同时监听(读取串口接收数据)这个设备返回的数据。如果返回的数据与“握手协议”所规定的相匹配,则可确定这个设备就是自家的设备。如果不匹配或根本没有收到任何数据(监听超时)则不是自家的设备。依次对上位机查找到的串口设备做这个判断过程,即可从“查找到的串口设备中”过滤出来“自家的串口设备”
六、上位机软件串口通信测试方法
既然开发串口设备相关的上位机软件,那么在开发软件中避免不了连接上硬件测试。那么肯定会遇到以下实际问题:
- 怎样确认所写的串口代码是否可以与硬件建立通信?
- 调试的时候想要看一下下位机收到的具体字符串(包括二进制数据),从而判断逻辑处理是否正确?
- 想要实时查看发送给串口的字符与二进制数据?从而判断软件执行进度与过程。
- 以上这些问题在下位机开发者帮助的情况下可解决。但是当对方忙或者其他原因导致没法帮助的时候,你怎么办?
以上这些问题是你在开发串口上位机软件过程中是实实在在的问题。
解决这个问题最方便以及实际的方法是使用“虚拟下位机”。
- 使用硬件设备虚拟设备模拟下位机:
- 方法一:
如果你使用的台式电脑主机后面有“RS232”插口(针),那么找到串口的Rx针和Rx针,把两者用电线连接起来即可。
连起来的结果是,你上位机软件给下位机发送过去的字符串会立即返回来,上位机就收到了自己刚刚发送出去的数据。虽然有局限性,但是在其他方式不方便的时候,也不失为一种手段。 - 方法二:
使用一个usb转串口线连接到电脑的usb口。然后同样短接转接头的Rx和Tx接头。即可实现和方法一同样的效果。
- 使用软件虚拟下位:
- 在windows操作系统下:
为了在解决以上问题的时候有更好的使用体验,可以使用“VSPD虚拟串口软件”。下载并安装好这个软件后。使用这个软件建立2个虚拟串口端口,然后把这两个虚拟串口使用软件的“连接”功能连接起来。在下载任意一个“串口小助手软件”,使用“串口小助手软件”来假装是下位机硬件设备。打开“串口小助手软件”并连接一个刚刚虚拟好的任意一个串口端口。然后打开自己的上位机软件连接剩余的一个虚拟串口端口。这样的话你上位机软件发送给下位机的所有字符串与数据都会在“串口小助手软件”中实时显示。并且可以通过“串口小助手软件”给自己的软件发送数据,用于模拟下位机硬件给上位机软件反馈的数据。 - 在Linux/MacOS操作系统下:
在非windows系统下没有类似VSPD的串口虚拟软件,但是在网上可以找到一个“虚拟串口python脚本”。不知道是哪位大牛写的,总之非常实用。代码很简短,附在下方(注意,在网络上发现有的版本是二进制的,且其中代码有好几千行,怀疑是假的或是带有木马的,本人对python不是特别了解。总之这里所附的代码是干净的,可以通过自己读代码来确定)
文件名字:linux_virtual_serials.py
使用方法,在命令行里面执行:python linux_virtual_serials.py
使用环境:python
#! /usr/bin/env python
#coding=utf-8
import pty
import os
import select
def mkpty():
# 打开伪终端
master1, slave = pty.openpty()
slaveName1 = os.ttyname(slave)
master2, slave = pty.openpty()
slaveName2 = os.ttyname(slave)
print '\nslave device names: ', slaveName1, slaveName2
return master1, master2
if __name__ == "__main__":
master1, master2 = mkpty()
while True:
rl, wl, el = select.select([master1,master2], [], [], 1)
for master in rl:
data = os.read(master, 128)
print "read %d data." % len(data)
if master==master1:
os.write(master2, data)
else:
os.write(master1, data)