Teensy 4.1通过Python读取SD卡大文件数据截断问题求助
问题分析与解决方案
核心问题根源
- Python串口默认接收缓冲区(约32KB)过小,Teensy快速发送大文件时会触发缓冲区溢出,导致后续数据丢失
readline()依赖换行符分割数据,若文件中换行稀少或缓冲区满后触发超时,会提前终止接收- Teensy端无流控机制,持续发送速度超过Python的处理能力;同时存在文件名解析的潜在错误(命令中的空格会被带入文件名)
解决方案1:增大Python串口接收缓冲区
取消注释缓冲区设置代码,将接收缓冲区调整为足够容纳大文件的尺寸:
def connect(self): self.ser = serial.Serial( self.serial_port, baudrate=self.baud_rate, timeout=self.timeout, bytesize=self.bytesize, stopbits=self.stopbit) # 增大接收缓冲区到1.6MB,可根据实际文件大小调整 self.ser.set_buffer_size(rx_size=1600000, tx_size=1600000) self.ser.reset_input_buffer()
解决方案2:替换readline()为批量读取逻辑
readline()效率低下且依赖换行符,改为循环读取所有字节直到收到结束标记:
def get_file(self, filename): # 去掉命令中的空格,避免Teensy解析文件名出错 self.write_command(f"get,{filename}\n") time.sleep(0.1) # 短延迟确保Teensy启动发送 data = b'' while True: # 每次读取尽可能多的字节,而非逐行等待 chunk = self.ser.read(self.ser.in_waiting or 1024) if not chunk: continue data += chunk # 检查是否包含结束标记 if b'EndOfMessage' in data: # 分离有效数据与结束标记 data = data.split(b'EndOfMessage')[0] break # 若需要按行处理,最后统一拆分 lines = data.split(b'\n') return lines
解决方案3:修复Teensy端文件名解析问题
Python发送的命令带空格会导致Teensy读取错误文件名,修改代码跳过空格:
else if (!strncmp(command, "get,", 4)) { ExFile putFile; uint8_t r; // 跳过"get,"后的所有空格,获取正确文件名 char* filename = &command[4]; while (*filename == ' ') filename++; // 打开文件 putFile = sd.open(filename, O_READ); if (putFile == NULL) { Serial.println("File not found"); return; } // 移除无用的ready变量,简化循环逻辑 while (putFile.available()) { r = putFile.read(); Serial.write(r); } // 关闭文件并发送结束标记 putFile.close(); Serial.println("EndOfMessage"); }
解决方案4:添加流控机制(针对超大文件)
如果文件尺寸超过缓冲区上限,通过"发送-确认"机制避免数据丢失:
Teensy端修改:
else if (!strncmp(command, "get,", 4)) { // ... 文件名解析与文件打开逻辑 ... const uint32_t CHUNK_SIZE = 32768; // 32KB块大小 uint8_t buffer[CHUNK_SIZE]; uint32_t bytesRead; while ((bytesRead = putFile.read(buffer, CHUNK_SIZE)) > 0) { Serial.write(buffer, bytesRead); // 等待Python发送确认信号 while (!Serial.available()); char ack = Serial.read(); if (ack != 'A') break; // 确认字符约定为'A' } putFile.close(); Serial.println("EndOfMessage"); }
Python端对应修改:
def get_file(self, filename): self.write_command(f"get,{filename}\n") time.sleep(0.1) data = b'' while True: chunk = self.ser.read(self.ser.in_waiting or 1024) if not chunk: continue data += chunk if b'EndOfMessage' in data: data = data.split(b'EndOfMessage')[0] break # 发送确认信号,允许Teensy继续发送下一块 self.ser.write(b'A') lines = data.split(b'\n') return lines
内容的提问来源于stack exchange,提问作者RvN
相关产品推荐
相关产品推荐

