Files
Uni-Lab-OS/unilabos/devices/motor/Utils.py
Junhan Chang c78ac482d8 Initial commit
2025-04-17 15:19:47 +08:00

88 lines
2.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import struct
import time
from typing import Union
from .Consts import Config
def calculate_modbus_crc16(data: bytes) -> tuple[int, int]:
"""
计算 Modbus RTU 的 CRC16 校验码,返回 (low_byte, high_byte)。
data 可以是 bytes 或者 bytearray
"""
crc = 0xFFFF
for byte in data:
crc ^= byte
for _ in range(8):
if (crc & 0x0001):
crc = (crc >> 1) ^ 0xA001
else:
crc >>= 1
# 低字节在前、高字节在后
low_byte = crc & 0xFF
high_byte = (crc >> 8) & 0xFF
return low_byte, high_byte
def create_command(slave_id, func_code, address, data):
"""
生成完整的 Modbus 通信指令:
- 第1字节: 从站地址
- 第2字节: 功能码
- 第3~4字节: 寄存器地址 (大端)
- 第5~6字节: 数据(或读寄存器个数) (大端)
- 第7~8字节: CRC校验, 先低后高
"""
# 按照大端格式打包B(1字节), B(1字节), H(2字节), H(2字节)
# 例如: 0x03, 0x03, 0x0191, 0x0001
# 生成的命令将是: 03 03 01 91 00 01 (不含 CRC)
command = struct.pack(">B B H H", slave_id, func_code, address, data)
# 计算CRC并将低字节、后高字节拼到末尾
low_byte, high_byte = calculate_modbus_crc16(command)
return command + bytes([low_byte, high_byte])
def send_command(ser, command) -> Union[bytes, str]:
"""通过串口发送指令并打印响应"""
# Modbus RTU 半双工,发送前拉高 RTS
ser.setRTS(True)
time.sleep(0.02)
ser.write(command) # 发送指令
if Config.OPEN_DEVICE:
# 如果是实际串口就打印16进制的发送内容
print(f"发送的数据: ", end="")
for ind, c in enumerate(command.hex().upper()):
if ind % 2 == 0 and ind != 0:
print(" ", end="")
print(c, end="")
# 发送完成后,切换到接收模式
ser.setRTS(False)
# 读取响应,具体长度要看从站返回,有时多字节
response = ser.read(8) # 假设响应是8字节
print(f"接收到的数据: ", end="")
for ind, c in enumerate(response.hex().upper()):
if ind % 2 == 0 and ind != 0:
print(" ", end="")
print(c, end="")
print()
return response
def get_result_byte_int(data: bytes, byte_start: int = 6, byte_end: int = 10) -> int:
return int(data.hex()[byte_start:byte_end], 16)
def get_result_byte_str(data: bytes, byte_start: int = 6, byte_end: int = 10) -> str:
return data.hex()[byte_start:byte_end]
def run_commands(ser, duration=0.1, *commands):
for cmd in commands:
if isinstance(cmd, list):
for c in cmd:
send_command(ser, c)
time.sleep(duration)
else:
send_command(ser, cmd)
time.sleep(duration)