树莓派ZeroW串口通讯

摘要:
准备串行通信电缆并将驱动程序安装到计算机上。树莓CPU内部有两个串行端口,一个是硬件串行端口,另一个是迷你串行端口。串行助手发送:hello_串行串行端口助手接收:received12字节˃˃hello_串口助手接收树莓发送串行端口发送的消息,表明硬件连接正常,串口收发功能正常。树莓派会在收到退出命令或某个时间限制后自动退出管理模式。在接收到广播数据帧后,树莓派会将其存储在一个int数组中,稍后将由其他程序处理。

>>[目录] 数据远程采集 Step by Step


电脑系统:WIN10

树莓派型号:Zero W

树莓派系统:Raspbian,2018-11-13-raspbian-stretch-lite.img

Python 2.7.3

软件&工具:sscom5.13.1(串口助手),串口通讯线,串口驱动(电脑用)


硬件配置


使用树莓派的GPIO15、GPIO16作为串口的TxD和RxD,另外还有附近的5V、GND。准备好串口通讯线,电脑上装好驱动。

image

树莓派CPU的内部有两个串口,一个是硬件串口,一个是迷你串口(mini-uart)。硬件串口的速率是稳定的,而迷你串口没有时钟源,由内核提供时钟参考源,由于内核本身的频率是变化的,导致迷你串口的速率不稳定。

系统默认把硬件串口分配给了蓝牙模块,迷你串口分配给了GPIO(GPIO15、GPIO16),这将影响串口通讯的可靠性,所以在安装树莓派系统的时候,交换了两个串口的分配:

image

pi@raspberrypi:~ $ ls -l /dev  #查看设备信息

image

ttyAMA0就是GPIO上的那个串口,系统默认它是作为console使用的,不过在安装树莓派系统的时候已经把这个配置给删掉了,现在可以作为普通的串口使用。

image

数据收发


先实现基本的数据收发功能,主要是为了测试接收和发送的通路是否正常。

新建一个测试文件 test_serial.py:

pi@raspberrypi:~ $ sudo nano test_serial.py

右键复制下面的内容,保存退出:

  1 # -*- coding:utf-8 -*-
  2 import serial
  3 import time
  4 
  5 def main():
  6   ser = serial.Serial("/dev/ttyAMA0", 19200)
  7   ser.flushInput()
  8   ser.flushOutput()
  9 
 10   while True:
 11     recv_num = ser.inWaiting()
 12 
 13     if recv_num > 0:
 14       recv_buffer = ser.read(recv_num)
 15       ser.flushInput()
 16 
 17       ser.flushOutput()
 18       ser.write('received ' + str(recv_num) + ' bytes >>  ')
 19       ser.write(recv_buffer)
 20 
 21     time.sleep(0.1)
 22 
 23 if __name__ == '__main__':
 24   main()

这段程序首先是打开串口0,波特率19200。然后在while循环中等待接收数据,接收到数据以后,把这个数据以及它的字节数发送出来。

每0.1s扫描一次,测试数据最好稍微短一点,否则就肯定被截断了。


运行 test_serial.py:

pi@raspberrypi:~ $ python test_serial.py

如果中途需要退出,比如去重新编辑文件,可以按Ctrl+Z

image


连接串口通讯线,打开串口助手(sscom),选择扫描到的COM口,设置波特率为19200,不勾选HEX显示、HEX发送,然后点击打开串口。

串口助手发送:hello_serial

串口助手接收:received 12 bytes >>  hello_serial

串口助手接收到的就是树莓派串口发送的,说明硬件连接正常,串口发送接收功能正常。

image


串口通讯


本项目中的串口通讯包含两种模式(互斥,默认为数据模式):

  • 管理模式下,树莓派接收配置参数,发送自身状态;
  • 数据模式下,树莓派接收设备的广播数据


管理模式

设计了4个管理指令,均为12字节,不够的就用*补足,可以根据实际的需要随意定义,指令的长度也可以不一样:

ADMIN_QUERY*查询状态:内部控制参数,磁盘信息,数据文件数量等
ADMIN_CONFIG配置内部控制参数:树莓派的编号,通讯协议,WIFI用户名和密码等
ADMIN_CLEAR*清空磁盘中的所有数据库文件
ADMIN_EXIT**退出管理模式

上电后一段时间内(可以设置为1-5分钟),树莓派接收到前3条指令后,均进入管理模式,树莓派在这个模式下每隔1s发送一次自身的状态。树莓派收到退出指令或者一定时间限制后,自动退出管理模式。


数据模式

广播包约0.5~2s发一帧,每帧数据约50~400字节,数据帧之间的空闲时间≥100ms。树莓派接收到广播数据帧后将其存在一个int数组里,其他程序后面会来处理这个数组。


新建一个测试文件 test_serial_comn.py:

pi@raspberrypi:~ $ sudo nano test_serial_comn.py

右键复制下面的内容,保存退出:

  1 # -*- coding:utf-8 -*-
  2 import serial
  3 import time
  4 import binascii
  5 
  6 DB_COMN_LENGTH = 400
  7 COMN_LENTH_MIN = 50
  8 COMN_LENTH_MAX = 400
  9 
 10 def DeviceConfigSet(str_config):
 11   print('DeviceConfigSet() function run...')
 12 
 13 def DeviceClear():
 14   print('DeviceClear() function run...')
 15 
 16 def GetDeviceStatus():
 17   print('GetDeviceStatus() function run...')
 18   return 'defined_device_status_string...'
 19 
 20 def main():
 21   global DB_COMN_LENGTH
 22   global COMN_LENTH_MIN
 23   global COMN_LENTH_MAX
 24 
 25   # initialize
 26   print('>> initialize...')
 27   ####
 28 
 29   is_data_received = False
 30   is_admin_mode_enabled = False
 31 
 32   in_admin_count = 0
 33   update_status_interval = 0
 34 
 35   comn_data = [0 for i in range(DB_COMN_LENGTH)]
 36   recv_num_last = 0
 37 
 38   ser = serial.Serial("/dev/ttyAMA0",19200)
 39   ser.flushInput()
 40   ser.flushOutput()
 41 
 42   print('>> wait for data or command...')
 43 
 44   while True:
 45     recv_num = ser.inWaiting()
 46     if recv_num > 0:
 47       time.sleep(0.01)
 48       recv_num_real = ser.inWaiting()
 49       while recv_num_real > recv_num:
 50         recv_num = recv_num_real
 51         time.sleep(0.01)
 52         recv_num_real = ser.inWaiting()
 53 
 54       recv = ser.read(recv_num_real)
 55 
 56       if in_admin_count < 3600 and 'ADMIN_CONFIG' == recv[0:12]:
 57         print('>> command received...')
 58         print(recv)
 59         is_admin_mode_enabled = True
 60         DeviceConfigSet(recv)
 61 
 62       elif in_admin_count < 3600 and 'ADMIN_QUERY*' == recv[0:12]:
 63         print('>> command received...')
 64         print(recv)
 65         is_admin_mode_enabled = True
 66 
 67       elif in_admin_count < 3600 and 'ADMIN_CLEAR*' == recv[0:12]:
 68         print('>> command received...')
 69         print(recv)
 70         is_admin_mode_enabled = True
 71         DeviceClear()
 72 
 73       elif True == is_admin_mode_enabled and 'ADMIN_EXIT**' == recv[0:12]:
 74         print('>> command received...')
 75         print(recv)
 76         is_admin_mode_enabled = False
 77 
 78       elif recv_num_real >= COMN_LENTH_MIN and recv_num_real <= COMN_LENTH_MAX:
 79         is_data_received = True
 80         for i in range(0, recv_num_real):
 81           comn_data[i] = int(binascii.b2a_hex(recv[i]), 16)
 82         for i in range(recv_num_real, recv_num_last):
 83           comn_data[i] = 0
 84         recv_num_last = recv_num_real
 85         print('>> data received...')
 86         print(comn_data)
 87 
 88       else:
 89         print('>> invalid data received!!!...')
 90         print(recv)
 91 
 92       ser.flushInput()
 93 
 94     # save data records if needed
 95     ####
 96 
 97     if in_admin_count < 3600:
 98       in_admin_count = in_admin_count + 1
 99     else:
100       is_admin_mode_enabled = False
101 
102     if True == is_admin_mode_enabled:
103       update_status_interval = update_status_interval + 1
104       if update_status_interval >= 20:
105         update_status_interval = 0
106         ser.flushOutput()
107         ser.write(GetDeviceStatus())
108 
109     # system tick
110     time.sleep(0.05)
111 
112 if __name__ == '__main__':
113   main()


代码说明


>> 切割通讯帧

通过帧之间的空闲时间来区分两个帧。软件正常每隔50ms扫描一次串口,看是否收到新的数据,如果收到了新的数据,认为新的一帧开始了,软件会每隔10ms扫描一次串口,直到在上个10ms内没有接受到新的数据,认为这一帧结束(扫描间隔要考虑波特率,19200对应的1个字节持续时间约为0.625ms)。一帧接收完成后,读取出全部接收到的数据,进入下一步处理。

主要是用这段代码来实现的:

  1 recv_num = ser.inWaiting()
  2 if recv_num > 0:
  3   time.sleep(0.01)
  4   recv_num_real = ser.inWaiting()
  5   while recv_num_real > recv_num:
  6     recv_num = recv_num_real
  7     time.sleep(0.01)
  8     recv_num_real = ser.inWaiting()
  9 
 10   recv = ser.read(recv_num_real)


>> 有效数据帧

为了考虑到采集系统的通用性,用数据帧的长度来进行过滤,给定一个有效数据帧的长度范围,超出的都认为是无效数据帧。

当然也可以把通讯协议做进去,可以配置参数里可以配置通讯协议类型,0默认是无协议,其他的自行定义。


>> 数据格式转换

串口接收到的数据 recv = ser.read(recv_num_real),read函数说明如下:

https://pythonhosted.org/pyserial/pyserial_api.html

image

依次打印recv[0]和recv:

  1 print('>> recv[0]...')
  2 print(recv[0])
  3 print('>> recv...')
  4 print(recv)
  5 print('>> data received...')
  6 print(comn_data)

image

串口助手发送的第一个字节是99(10进制),recv[0]打印出来是c,这个是ascii格式的:

image

要把ascii格式的数据转换为int格式的数据:

  1 import binascii
  2 
  3 comn_data[i] = int(binascii.b2a_hex(recv[i]), 16)

通过binascii.b2a_hex()函数来接收串口数据recv[i],把这个字节转换为16进制的数据;

通过int(x [,base])把刚刚得到的16进制数据转换为int格式的数据。



运行 test_serial_comn.py,显示初始化完成,等待接收数据或指令:

pi@raspberrypi:~ $ python test_serial_comn.py

image


测试管理指令


树莓派收到[ADMIN_QUERY*]指令后,进入管理模式,开始每隔1s发送一次自身的状态;

树莓派收到[ADMIN_EXIT**]指令后,退出管理模式image

树莓派收到[ADMIN_CLEAR*]指令后,进入管理模式,调用DeviceClear()函数清空磁盘中的数据文件,并开始每隔1s发送一次自身的状态;

树莓派收到[ADMIN_CONFIG]指令后,进入管理模式(之前已经在了),调用DeviceConfigSet()函数配置参数,并开始每隔1s发送一次自身的状态;

树莓派收到[ADMIN_EXIT]非法指令,提示收到非法指令;

树莓派收到[ADMIN_EXIT**]指令后,退出管理模式image


测试广播数据


测试包长度为222字节,16进制显示:

63 78 12 10 05 32 01 00 00 00 00 00 00 10 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2E 0A 27 00 00 00 00 00 5A 00 26 00 5A 00 26 00 5A 00 26 00 00 00 00 11 F8 08 FC 0C D0 05 5A 08 5C 03 84 04 9C 01 F4 00 00 00 00 00 52 00 26 10 68 08 FC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2A 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00 00 00 00 03 E8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 F8 08 FC 11 F8 08 FC

连续测试10个包,发送间隔500ms,主要是看一下接收到的数据包是否完整(软件中写了数组长度为400字节,不够的就补零)。

image


免责声明:文章转载自《树莓派ZeroW串口通讯》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇robotframework的学习笔记(十三)------Robot Framework常用库简介Unicode 字符集与它的编码方式下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

java之内部类详解

      序言         有位小同学要我写一篇这个的总结,我说那好吧,那就动手写总结一下这个内部类的知识,感觉这个在面试中也会经常遇到,内部类、反射、集合、IO流、异常、多线程、泛型这些重要的基础知识大家都比较容易记不住。大概是自己平常用的比较少,所以经常性的会忘记,现在通过博文的方式记录下来,以后忘记可以回过头来自己看。             ...

使用memset()要注意

原型如下: ptr是要写入的内存块的指针,value是要写入的值,num是从ptr指向的首地址开始一共要写入的字节数。 要注意num传入的参数 错误示范:    实际上sizeof(InDegree)是4,等于sizeof(int)。也就是说这里的num参数只是一个指针的大小,没有完成初始化工作,除了第一个数组元素外,剩下的都是随机值。 应该改成:...

php中访问文件或文件夹相关操作

1、filetype() //可以输出相关文件类型,如:dir(表示目录)/file(表示文件) 如:echo filetype("c:/") 输出结果为:dir 如:echo filetype("f:/num.txt") 输出结果为:file 2、stat() //获得指定文件名参数目标文件基本属性 $stt=stat("f:/num.txt"); pr...

Android Studio的串口通讯开发

基于android-serialport-api实现 前言RS232标准接口UARTRS232与UART转接下载 NDK 和构建工具创建支持 C/C++ 的新项目编译C/C++代码串口通讯原理关于校验位HexString与Bytes的转换参考 前言 软件代码写久了,总会对嵌入式开发感兴趣,因为软件的东西写来写去看不见摸不着,而嵌入式硬件开发,可以捣...

树莓派搭建Nexus2私服

使用树莓派搭建Nexus2私服需要的材料有: 树莓派3B+(或者4B) 移动硬盘一个 树莓派相关文章: 树莓派搭建nexus2.x私服(本文) 树莓派搭建视频监控平台 树莓派视频监控平台实现录制归档 树莓派实现人脸打卡机 1. 下载nexus2.x安装包 由于nexus2.x官方的启动环境并不支持arm架构的树莓派,所以这里采用tomcat7 +...

去掉字符串中的某几位

一招吃遍力扣四道题,妈妈再也不用担心我被套路啦~ lucifer发布于2020-06-163.7kPythonPython3 我花了几天时间,从力扣中精选了四道相同思想的题目,来帮助大家解套,如果觉得文章对你有用,记得点赞分享,让我看到你的认可,有动力继续做下去。 这就是接下来要给大家讲的四个题,其中 1081 和 316 题只是换了说法而...