pySerial在读取三次后冻结

0 投票
2 回答
2917 浏览
提问于 2025-04-16 07:18

我们正在编写一个程序,发现如果在串口发送数据的同时,它也试图向我们发送数据,程序就会卡死(我们的Python代码和HyperTerminal在测试时都会崩溃)。在HyperTerminal中慢慢输入数据(每次输入之间间隔超过0.5秒)是可以正常工作的,但如果快速输入就会崩溃。因此,我们尝试加入一个比0.5秒更长的时间延迟,但问题依然没有解决。

这是我们的测试代码。

import serial
import time

ser = serial.Serial("COM1")
ser.baudrate=2400

while 1:
    for i in range(23):
        ser.write(0x41)       
        time.sleep(.5)
        print("ok")

    rec = ser.read()
    rec2 = ser.read()
    rec3 = ser.read()
    print(rec)
    print(rec2)
    print(rec3)

    for i in range(23):
        data = ser.read()
        print(data)
        print("ok")
    time.sleep(5)

这是我们的接收数据函数。之前我们每次接收到一个字符时都会发送“ok”(这也是我们发现程序在第三次迭代后会卡住的原因)。我们把这个“ok”放到循环外面,想看看是不是这个导致了问题,但结果并不是。用现在的代码根本没有发送“ok”。

unsigned char receiveData(unsigned char *rxData, int length){
  // 1. Flag bit, RCIF, will be set when reception is complete and an interrupt will be generated if enable bit, RCIE, was set.
 char send[3] = "ok";

 int index = 0;

 if(rxData==(void*)0 || rxInitialized==FALSE) return FAILURE;
 while(index<length){
  while(PIR1bits.RCIF==0);       
  rxData[index]= RCREG;
  Delay1KTCYx(5);
  index++;
 }
    configureTransmission();
    sendData(send,3);

  // 2. Read the RCSTA register to get the 9th bit (if enabled) and   determine if any error occurred during reception.
  // 3. Read the 8-bit received data by reading the RCREG register.
  // 4. If any error occurred, clear the error by clearing enable bit   CREN.
 return SUCCESS;
}

2 个回答

1

PIC的通信是否使用了串口的RTS/CTS线路?可能是因为PIC需要某种流量控制,而你发送数据的速度太快,没有流量控制。了解一下PIC的限制,如果需要的话,可以在打开端口时启用流量控制

2

(这个回答假设你正在使用PIC16,因为某些寄存器的名称暗示了这一点。)

简单来说,这看起来像是一个缓冲区溢出的问题,加上在receiveData函数中的一个bug。发送三个字符后程序卡住的现象,可以通过手册第117页的内容来解释:

可能会接收到两个字节的数据并将其转移到RCREG FIFO中,同时第三个字节开始移动到RSR寄存器。

这就解释了为什么是数字三。

让我们通过你的PIC代码来看看一个场景(仅作为示例)。第一次执行时:

// One character already in RCREG - RCIF set
while(PIR1bits.RCIF==0);
// Reads ONE character - RCIF clear
rxData[index]= RCREG;
// While waiting here, two more characters are received - RCIF set
Delay1KTCYx(5);
index++;

第二次执行时:

// RCIF set from before
while(PIR1bits.RCIF==0);
// Reads ONE character - RCIF STILL set, ONE character remains in UART FIFO!
rxData[index]= RCREG;
// While waiting here, three more characters are received
// RCIF set, RCREG fills up and the third character is discarded!
Delay1KTCYx(5);
index++;

现在,循环的其余部分会继续从RCREG读取,直到index == length,但由于在UART FIFO满的时候有一些字符被丢弃,所以你永远无法达到这个条件,程序看起来就像卡住了一样!

更有可能的是,在你到达这个函数之前就已经接收到了字符,因此UART FIFO在你到达之前就已经满了。

有几种方法可以解决这个问题。

  1. 在中断中处理,这样可以更快地将接收到的字符放入缓冲区。
  2. 使用循环从RCREG读取:while(RCIF) rxData[index]= RCREG; 这样可以确保在从UART缓冲区读取时清空缓冲区,但这并不能防止在这个函数外部或延迟期间发生溢出。
  3. 检查OERR标志 - 如果它被设置,说明发生了错误,重新开始。
  4. 设置一个停止字符或开始字符(例如,行结束符、标点符号等),用来告诉你一个有效命令的开始或结束。如果你收到了两个开始字符而没有停止字符,或者其他混乱的组合,假设你处于一个错误状态并重新开始。

一些额外的建议:你可能会疯狂地尝试在你的PIC代码中处理和补偿每一个丢失的字符或类似的问题,但最终这只是另一个通信错误。PIC代码中的优先事项应该是:快速恢复错误,而不是锁死。错误检测和合理的恢复应该由客户端代码来处理,这样会简单得多。

撰写回答