pySerial在读取三次后冻结
我们正在编写一个程序,发现如果在串口发送数据的同时,它也试图向我们发送数据,程序就会卡死(我们的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 个回答
(这个回答假设你正在使用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在你到达之前就已经满了。
有几种方法可以解决这个问题。
- 在中断中处理,这样可以更快地将接收到的字符放入缓冲区。
- 使用循环从
RCREG
读取:while(RCIF) rxData[index]= RCREG;
这样可以确保在从UART缓冲区读取时清空缓冲区,但这并不能防止在这个函数外部或延迟期间发生溢出。 - 检查
OERR
标志 - 如果它被设置,说明发生了错误,重新开始。 - 设置一个停止字符或开始字符(例如,行结束符、标点符号等),用来告诉你一个有效命令的开始或结束。如果你收到了两个开始字符而没有停止字符,或者其他混乱的组合,假设你处于一个错误状态并重新开始。
一些额外的建议:你可能会疯狂地尝试在你的PIC代码中处理和补偿每一个丢失的字符或类似的问题,但最终这只是另一个通信错误。PIC代码中的优先事项应该是:快速恢复错误,而不是锁死。错误检测和合理的恢复应该由客户端代码来处理,这样会简单得多。