STM32串口通信指南:Python协议解包和通讯协议讲解(详细教程

介绍完之前的STM32单片机串口外设的开启,接下来就开始讲解如何用Python来接收单片机传过来的数据。在此之前呢,我们简要理解一下上位机与下位机通讯时用所用到的通讯协议的结构。

一、通讯协议

个人理解的通讯协议指的就是下位机与上位机的一种沟通的方式,简单的说,通讯协议就是将上位机发送的一帧数据规定成一个固定的形式发送给下位机,下位机再以这种形式拆解出其中包含的信息,这样就能实现上位机与下位机的通讯了,相对的,下位机与上位机通讯也是如此。

了解了它的含义之后,我们就要知道通讯协议的结构,一般包括,帧头、数据、校验位、帧尾这几个基本部分。

如上图所示,这是一帧的数据结构,0XAA为本数据帧的帧头, 0X01-0X04为电机的标号,代表第一个电机,一共四个电机,0XDD为数据的帧尾,Float是我们需要的数据,也就是要解析出来的数据。float为浮点型,占四个字节,所以一帧的数据一共为19个字节。

二、数据接收

Python数据接收需要用到一个第三方模块pySerial,如果要使用 pySerial 模块,首先必须保证 Python 版本高于 Python 2.7 或者 Python 3.4,安装pySerial很简单,只需输入以下命令即可。

pip install pyserial

安装完成后,在Python中写入import serial即可导入模块

我们先配置串口号、设置波特率,并封装一个串口开启函数,注意,串口号我们可以在电脑段查询,每个电脑的USB串口都有独立的串口号,要更改相应的串口号才可以。

import serial

ser = serial.Serial()
def port_open():
    ser.port = 'COM5'          #串口号
    ser.baudrate = 115200      #波特率
    ser.open()                 #串口开启
    if ser.isOpen():           #查询串口是否开启成功
        print("串口打开成功")
    else:
        print("串口打开失败")

串口关闭函数

def port_close():
    ser.close()               #串口关闭
    if (ser.isOpen()):        #查询串口是否关闭成功
        print("串口关闭失败")
    else:
        print("串口关闭成功")

串口读取函数

注:ser.in_waiting是计算串口接收的字节数的函数

def receive_data():
    while True:
        if ser.in_waiting:                  #查询串口是否有数据传入
            data = ser.read(ser.in_waiting) #读取串口数据
        else:
            break
        print(data)
        return data

串口写入函数

def write_data(data_to_send):
    try:
        # 将数据写入串口
        if i==1:
            ser.write(data_to_send)         #发送数据  
        print("数据发送成功:", data_to_send)
    except Exception as e:
        print("发送数据时出现错误:", e)

三、数据解析

到这一步,我们就可以根据上文设计的通讯协议进行数据解析了,首先我们设置一个字典,方便再接收数据帧头时判断是哪一个电机的数据。

dianji1 = 1
dianji2 = 2
dianji3 = 3
dianji4 = 4

xieyi_list = {
           # 数据总长度,帧尾,电机编号    
    'aa01': (       19, 'dd', dianji1),
    'aa02': (       19, 'dd', dianji2),
    'aa03': (       19, 'dd', dianji3),
    'aa04': (       19, 'dd', dianji4),
}

一帧的数据解析

这个函数是先把接收到的数据转为字符串类型,再提取帧头0XAA0X01(以电机1为例),

注:

1.这里的head_len*2,是因为我们把这一帧的数据转化为了字符串,而我们的数据是16位的,所以,一帧头对应的字符串是两个。

2.struct.unpack()用于将字节串转换为元组。这个函数在处理二进制数时非常有用,本文就是利用它将接收到的数据转为自己想要的数据类型。它的具体函数是:

struct.unpack(format, buffer)

其中,format是一个字符串,用于指定如何解析bufferformat中的每个字符都代表一种数据类型和一种解析方式。buffer是要解析的字节串。

一些常用的格式字符:

  • x:填充字节
  • b:有符号字符
  • B:无符号字符
  • h:有符号短整数
  • H:无符号短整数
  • i:有符号整数
  • I:无符号整数
  • l:有符号长整数
  • L:无符号长整数
  • q:有符号长长整数
  • Q:无符号长长整数
  • f:浮点数
  • d:双精度浮点数
import re
import struct


def one_message_(message, head_len, tail_len, codes, ):
    # 一帧数据解析
    # message:   传进来的数据帧 byets
    # codes:     数据类型    str
    # head_len:  帧头长度    int
    # tail_len:  帧尾长度    int
    if isinstance(message, bytes):
        message = message.hex()
        hard_char = message[:head_len * 2]                       # 读取帧头'aa01'
        if hard_char in xieyi_list:                              # 如果'aa01'在字典里
            data_char = message[head_len * 2:-tail_len * 2]      # 读取数据
            tail_char = message[-tail_len * 2:]                  # 读取帧尾据
            data_divide = re.findall('.{8}', data_char)          # 等长数据划分
            data = [struct.unpack(codes, bytes.fromhex(i))[0] for i in data_divide]  # 将16位数据转为float类型
    return hard_char, data, tail_char

主函数 


if __name__ == '__main__':
    port_open()
    message = receive_data()
    one_message_(message,head_len=2, tail_len=1, codes='

四、结语

目前,我们已经创建好了一帧数据的解析,下章,我将介绍如和连续接解析下位机发送过来的数据。

对于STM32串口外设开启的方法请看这一篇:https://blog.csdn.net/m0_73816319/article/details/135656100?spm=1001.2014.3001.5502

你可能感兴趣的:(python,单片机,嵌入式实时数据库,stm32,嵌入式硬件)