USB serial port communication, Raspberry Pi uses GPIO port to send and receive Python code of hexadecimal array

The industrial computer and the Raspberry Pi use USB to TTL serial port communication, and the latter uses the GPIO port to send and receive Python codes of hexadecimal arrays

wiring

The USB to TTL module is used, the industrial computer of the Linux system is connected to the USB socket, the TTL module outputs RX, TX, VCC, and GND, and the GPIO14 port of the Raspberry Pi is RXD0 connected to the TX of the TTL serial port.

USB to TTL-CH340 module

Use Python's RPi.GPIO library on the Raspberry Pi, and configure GPIO14 (RXD0) as the receiving pin.
Write the code for receiving data on the Raspberry Pi, check the state of GPIO14 (RXD0) through GPIO.input(14), if it is high, it means that there is data reception, and then use ser.read(ser.in_waiting) to read the serial port reception data in the buffer.

letter of agreement

Serial port communication sends a string of data, including 2 headers and a hexadecimal character array with a total length of 6, expressed as uint8 data[6]. The first 2 bytes of each piece of data are 0xAA, the beginning of 0xAA; the third byte is the type of instruction; the 4th and 5th bits are instructions, which are encoded as 2-bit uint8; the first 5 bits are encoded in CRC8 Check to get the 6th digit. A typical data format conforming to the specification is as follows:
0xAA , 0xAA , 0x09 , 0x07 , 0x01 , 0x32
0x07 , 0x01 are coded instructions.

send

Is there a python version of the program that can send instructions for reference. Each sub-function needs to be written as a separate function, including such as opening the serial port, sending instructions, encoding of instructions, CRC check, etc.

The following is a sample Python program for ChatGPT
, which is used to send serial data, perform command encoding and CRC check. The program assumes the use of the pyserial library for serial communication.

# -*- coding: UTF-8 -*-
#!/usr/bin/python3 python3
import serial
import struct
import time


# 配置串口参数
port = '/dev/ttyUSB0'  # 串口设备路径
baudrate = 9600  # 波特率

# 打开串口
ser = serial.Serial(port, baudrate)

# 定义指令类型的常量
INSTRUCTION_TYPE = 0x09

# CRC8校验函数
def crc8(data):
    crc = 0x00
    polynomial = 0x8C  # CRC-8多项式

    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x80:
                crc = (crc << 1) ^ polynomial
            else:
                crc <<= 1

    return crc & 0xFF

# 将时间秒数编码为两位16进制数字
def encode_seconds(seconds):
    seconds_hex = hex(seconds)[2:].zfill(2)  # 将秒数转换为两位的十六进制字符串
    return int(seconds_hex, 16)  # 将十六进制字符串转换为整数


# 编码指令
def encode_instruction(data):
    encoded_data = []
    for value in data:
        encoded_data.append(value & 0xFF)
    return encoded_data

# 发送指令
def send_instruction(instruction):
    # 构建指令
    data = bytearray()
    data.extend([0xAA, 0xAA])  # 前导字节
    data.append(INSTRUCTION_TYPE)  # 指令类型
    data.extend(encode_instruction(instruction))  # 编码后的指令数据
    
    # 计算校验码
    crc = crc8(data[:5])  # 计算校验码时不包括最后一个字节(校验码本身)
    data.append(crc)  # 添加校验码的低字节

    return data


try:
    # 主循环
    while True:
        # 测试发送指令

        # instruction = [0x07, 0x08]  # 指令数据
        # 获取当前时间的秒数
        seconds = int(time.time())
        # print(seconds)
        # 发送时间秒数编码
        instruction = encode_seconds(seconds).to_bytes(4, 'big')# 发送一个字节的数据,高位在前


        data = send_instruction(instruction[2:4])
        formatted = ' '.join(f'{
      
      byte:02X}' for byte in data)
        print(formatted)
        # 发送指令
        ser.write(data)

        # # 接收指令
        # response = receive_command()
        # if response:
        #     print('Received:', response)

        # 休眠一段时间,控制发送频率
        time.sleep(0.2)  # 休眠0.2秒

except KeyboardInterrupt:
    # Ctrl+C被按下,执行异常处理
    
    ser.close()
    print("程序终止,关闭串口")

except Exception as e:
    # 其他异常处理
    
    print("发生异常:", str(e))
    ser.close()

In the above example, we define a encode_instruction()function to encode the instruction data, limit each element to the range of 0-255, and return the encoded data. Then we build the instruction, add the leading bytes, the instruction type, the encoded instruction data, and calculate the checksum. Finally, we use ser.write()the function to send the instruction data to the serial port.

Please note that in practical applications, you may also need to add functions such as error handling, timeout setting, data processing, etc. to ensure stable and reliable serial communication. The above example only provides a basic framework, and the specific implementation may need to be adjusted and extended according to your specific needs.

take over

Serial port communication receives a string of data, including 2 headers and a hexadecimal character array with a total length of 6, expressed as uint8 data[6]. The first 2 bytes of each piece of data are 0xAA, the beginning of 0xAA; the third byte is the type of instruction; the 4th and 5th bits are encoded instructions, which need to be parsed into int16 numbers; CRC16 is used to compare the front 5 digit checksum, get the 6th digit, MODBUS algorithm.

Is there any python version of the program that can be referred to. Each sub-function needs to be written as a separate function, including
ChatGPT such as opening the serial port, receiving instructions, instruction analysis, CRC check, etc.

The following is a sample Python program for receiving serial port data, performing CRC check and parsing instructions. The program assumes the use of the pyserial library for serial communication.

# -*- coding: UTF-8 -*-
#!/usr/bin/python3 python3
import serial
import struct
import RPi.GPIO as GPIO


# 配置GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(14, GPIO.IN)  # GPIO14作为接收引脚

# 配置串口参数
port = '/dev/serial0'  # 树莓派上UART接口所对应的串口设备路径
baudrate = 9600  # 波特率
# 打开串口
ser = serial.Serial(port, baudrate)

# 配置GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(14, GPIO.IN)  # GPIO14作为接收引脚

# 定义指令类型的常量
INSTRUCTION_TYPE = 0x09

# CRC8校验
def crc8(data):
    crc = 0x00
    polynomial = 0x8C  # CRC-8多项式
    
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x80:
                crc = (crc << 1) ^ polynomial
            else:
                crc <<= 1
    
    return crc & 0xFF

# 解析指令
def parse_instruction(data):
    if len(data) != 6:
        print("Invalid instruction length")
        return
    
    # 校验头部
    if data[0] != 0xAA or data[1] != 0xAA:
        print("Invalid header")
        return

    # 校验指令类型
    if data[2] != INSTRUCTION_TYPE:
        print("Invalid instruction type")
        return

    # 校验CRC
    crc = crc8(data[:5])  # 计算校验码时不包括最后一个字节(校验码本身)
    if crc != data[5]:
        print("CRC check failed")
        return
    
    # 获取指令数据
    instruction = data[3:5]
    
    # 执行相应的指令逻辑
    process_instruction(instruction)

# 处理指令逻辑
def process_instruction(instruction):
    # 按照具体需求进行指令处理
    cmd16 = struct.unpack('<h', bytearray(instruction))[0]  # 解析为int16数字
    print("Received Instruction:", cmd16)

try:
    # 接收数据
    while True:
        if GPIO.input(14):  # 检查GPIO14状态,判断是否有数据接收
            # data = ser.read(ser.in_waiting)  # 一直读取数据
            data = ser.read(6)  # 读取固定长度的数据
            # 接收数据
            if len(data) == 6 and data[:2] == b'\xAA\xAA':
                parse_instruction(data)

except KeyboardInterrupt:
    # Ctrl+C被按下,执行异常处理
    
    ser.close()
    print("程序终止,关闭串口")

except Exception as e:
    # 其他异常处理
    
    print("发生异常:", str(e))
    ser.close()

In the above example, we defined a parse_instruction()function to parse the command data received. First, we check if the instruction length is 6 bytes and do a CRC check. If the verification fails or the command type does not match, the corresponding error message will be output. If the verification is passed, we parse the instruction data and call process_instruction()the function to execute the corresponding instruction logic.

Please note that the above examples only provide a basic framework, you may need to adjust and expand according to your specific needs. In practical applications, you may also need to add functions such as error handling, timeout setting, and data processing to ensure stable and reliable serial communication.

How does serial communication control the frequency of data sending and analysis?

ChatGPT

In serial communication, you can use different methods to control the frequency of data transmission. Here are a few common methods:

  • Delayed Send: Adds a fixed delay between sending each packet. You can use time.sleep()functions to add a delay between sending packets to control how often you send them. For example, time.sleep(0.1)will delay 100 milliseconds after sending a packet. This method is not recommended.

  • Scheduled sending: Use timers to send data periodically. You can use Python threading.Timerclasses to create a timer and send data in the timer callback function.

In serial communication, using threading.Timerthe frequency of control data reception and analysis can be achieved through the following steps:

  • Create a timer object and specify the timer interval.
  • Execute the logic of data receiving and parsing in the timer callback function.
  • Start the timer so that it starts to execute the callback function regularly.

The following is an example that demonstrates how to use threading.Timer to control the frequency of serial port data reception and parsing:


import serial
import threading

# 配置串口参数
port = '/dev/ttyUSB0'  # 串口设备路径
baudrate = 9600  # 波特率

# 打开串口
ser = serial.Serial(port, baudrate)

# 定义数据接收和解析的函数
def receive_and_parse_data():
    # 接收数据
    data = ser.readline().strip()

    # 解析数据
    # TODO: 在这里添加你的数据解析逻辑
    print("Received data:", data)

    # 重新启动定时器,控制数据接收和解析的频率
    timer = threading.Timer(1.0, receive_and_parse_data)
    timer.start()

# 启动定时器,控制数据接收和解析的频率
timer = threading.Timer(1.0, receive_and_parse_data)
timer.start()

# 阻塞主线程,保持程序运行
while True:
    pass

# 关闭串口
ser.close()

In the above example, receive_and_parse_data()the function is called by the timer to receive and parse the serial port data regularly. The interval of the timer is 1.0 seconds, you can adjust it according to your needs.

Regardless of using delayed sending or timing sending, the frequency of sending data can be adjusted according to actual needs. Please note that the actual sending frequency may be limited by factors such as serial port transmission rate, packet size and processing power of the target device. Therefore, it needs to be adjusted and tested according to specific application scenarios and requirements.

Guess you like

Origin blog.csdn.net/NICAI001/article/details/131617817