0-255数值转单个字符异常及UART传输STM32接收问题求助
串口字节传输问题排查与解决
问题描述
我尝试将0至255的数值转换为单个字符,使用chr()函数时发现例如255这类数值会被转换为双字符字符串。将这些字符拼接成字符串后,通过bytes(string, 'UTF-8')转换为字节流并发送至STM32的UART串口,但接收端无任何响应。也曾尝试构建包含0-255 uint8类型元素的字节数组进行传输,依然无法在STM32端接收到数据。
更新:当使用LATIN-1编码时,0-255的数值可以以单字节形式传输,但单独发送LATIN-1编码字节数组中的单个元素时却无法正常工作,请问这是什么原因?
原始代码片段
#!/usr/bin/python import os import sys import serial def asbyte(v): return chr(v & 0xFF) def printStdErr(*objs): # print("", *objs, file=stderr) print("") class LightYModem: soh = 1 # 128 byte blocks stx = 2 # 1K blocks eot = 4 ack = 6 nak = 0x15 ca = 0x18 # 24 crc16 = 0x43 # 67 abort1 = 0x41 # 65 abort2 = 0x61 # 97 packet_len = 1024 expected_packet_len = packet_len+5 packet_mark = stx def __init__(self): self.seq = None self.ymodem = None def blocking_read(self): ch = '' while not ch: ch = self.ymodem.read(1) printStdErr("read %d " % ord(ch)) return ch def write(self, packet): #print(packet) b = bytes(packet, 'UTF-8') print(b) self.ymodem.write(b) return len(packet); def flush(self): pass #self.ymodem.flush() def _read_response(self): ch1 = self.blocking_read() ch1 = ord(ch1) printStdErr("response %d" % (ch1)) if ch1==LightYModem.ack and self.seq==0: # may send also a crc16 ch2 = self.blocking_read() elif ch1==LightYModem.ca: # cancel, always sent in pairs ch2 = self.blocking_read() return ch1 def transfer(self, file, ymodem, output): self.ymodem = ymodem """ file: the file to transfer via ymodem ymodem: the ymodem endpoint (a file-like object supporting write) output: a stream for output messages """ file.seek(0, os.SEEK_END) size = file.tell() file.seek(0, os.SEEK_SET) response = self.send_filename_header("binary", size) while response==LightYModem.ack: response = self.send_packet(file, output) file.close() if response==LightYModem.eot: self._send_close() return response def _send_ymodem_packet(self, data): # pad string to 1024 chars data = data.ljust(LightYModem.packet_len) seqchr = asbyte(self.seq & 0xFF) print("seqchr : "+str(len(seqchr))) seqchr_neg = asbyte((255-self.seq) & 0xFF) print("seqchr_neg : "+str(len(seqchr_neg))) crc16 = '\x00\x00' print("bytes "+ str(bytes([255]))) packet = asbyte(LightYModem.packet_mark) + seqchr + seqchr_neg + data + crc16 if len(packet)!=LightYModem.expected_packet_len: raise Exception("packet length is wrong!") self.packet = packet written = self.write(packet) printStdErr("sent packet data, flush..."+str(written)) self.flush() printStdErr("wait response..") response = self._read_response() if response==LightYModem.ack: ("sent packet nr %d " % (self.seq)) self.seq += 1 return response def send_packet(self, file, output): response = LightYModem.eot data = file.read(LightYModem.packet_len) if len(data): response = self._send_ymodem_packet(data) return response def send_filename_header(self, name, size): self.seq = 0 packet = name packet += asbyte(0) packet += str(size) packet += ' ' return self._send_ymodem_packet(packet) def transfer(self, file, ymodem, output): self.ymodem = ymodem """ file: the file to transfer via ymodem ymodem: the ymodem endpoint (a file-like object supporting write) output: a stream for output messages """ file.seek(0, os.SEEK_END) size = file.tell() file.seek(0, os.SEEK_SET) response = self.send_filename_header("binary", size) while response==LightYModem.ack: response = self.send_packet(file, output) file.close() if response==LightYModem.eot: self._send_close() return response def ymodem(args): ser = serial.Serial('COM24', 115200, serial.EIGHTBITS, serial.PARITY_NONE, serial.STOPBITS_ONE) os.chdir(r'D:\projects\testprojects\code\Test1\config') file = open(os.getcwd()+'\testfile.bin', 'rb') result = LightYModem().transfer(file, ser, sys.stderr) file.close() print("result: " + str(result)) try: while (True): print(ser.read()) except: pass print("Done") if __name__ == '__main__': ymodem(sys.argv)
更新后的测试代码片段
seqchr ='\x00' seqchr_neg = '\xFF' packet = seqchr + seqchr_neg ... def write(self, packet): bh = bytearray(packet, 'LATIN-1') ch = bytes(packet[0], 'LATIN-1') print(ch) print(bh[0]) self.ymodem.write(bh )#receive only zero on my STM self.ymodem.write(ch)# receive the 0x02 on my STM
核心原因分析
- UTF-8编码不匹配:UTF-8对0x80以上的字符会采用多字节编码(比如0xFF会被编码为
0xC3 0xBF两个字节),这和UART单字节传输的需求完全冲突,STM32端接收的是错误的多字节数据,无法正确解析。 - 字符串与字节的混淆:Python的字符串是Unicode字符序列,并非原始字节序列。用
chr()生成的是Unicode字符,转码时会受编码规则影响,不能直接用来表示底层字节。 - 单元素传输的写法错误:更新代码中
bytes(packet[0], 'LATIN-1')属于误用——虽然能得到单字节,但逻辑绕弯且容易出错,正确的单字节生成方式应该直接基于数值构造。
解决方案
1. 全程使用字节类型处理,放弃字符串中转
串口传输本质是字节流,直接用bytes或bytearray类型操作,避免字符串转码的干扰:
- 替换
asbyte函数:
def asbyte(v): return bytes([v & 0xFF]) # 直接返回单字节bytes对象
- 修改
write方法,直接写入字节数据:
def write(self, packet): # 统一转成字节类型发送 if isinstance(packet, str): packet = packet.encode('LATIN-1') elif not isinstance(packet, (bytes, bytearray)): packet = bytearray(packet) self.ymodem.write(packet) self.ymodem.flush() # 确保数据全部发送 return len(packet)
2. 修正YModem包的构建逻辑
直接用字节拼接代替字符串拼接,避免编码问题:
def _send_ymodem_packet(self, data): # 确保data是字节类型 if isinstance(data, str): data = data.encode('LATIN-1') # 补全到1024字节 data = data.ljust(LightYModem.packet_len, b'\x00') seq = self.seq & 0xFF seq_neg = (255 - seq) & 0xFF # 直接构建字节包 packet = bytes([LightYModem.packet_mark]) + bytes([seq]) + bytes([seq_neg]) + data + b'\x00\x00' if len(packet) != LightYModem.expected_packet_len: raise Exception("packet length is wrong!") written = self.write(packet) printStdErr("sent packet data, flush..."+str(written)) printStdErr("wait response..") response = self._read_response() if response == LightYModem.ack: printStdErr("sent packet nr %d " % (self.seq)) self.seq += 1 return response
3. 正确发送单个字节
如果需要单独发送单个字节,直接用数值构造字节对象:
# 发送单个字节0x00 self.ymodem.write(bytes([0x00])) # 发送单个字节0xFF self.ymodem.write(bytes([0xFF]))
额外注意事项
- 确认STM32端的串口配置与Python端完全一致:波特率115200、8位数据位、无校验、1位停止位。
- 发送后必须调用
flush(),确保所有数据都被写入串口缓冲区并发送。 - 检查STM32端的UART接收逻辑:确认缓冲区大小足够,且能正确解析YModem协议的帧头、序列号等字段。
内容的提问来源于stack exchange,提问作者user3719581
相关产品推荐
相关产品推荐

