pySerial读取3次后冻结

发布于 2024-10-02 21:00:16 字数 1498 浏览 0 评论 0原文

我们正在编写一个图片,我们已经诊断出,如果我们在串行端口尝试向我们发送数据时将数据发送到串行端口,则程序将锁定(测试时我们的 python 代码和超级终端都会崩溃)。它在超级终端中运行并缓慢输入(笔画之间>.5秒),并且当敲击键盘时会崩溃。所以我们所做的是引入一个超过 0.5 秒的 time.sleep,但它仍然不起作用。

这是我们的测试代码。

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”(这就是我们知道它在 3 次迭代后冻结的方式)。我们将其放在循环之外,看看这是否导致了问题,但事实并非如此。它根本不使用此代码发送“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;
}

We are programming a pic and we've diagnosed that if we send data to serial port while its trying to send data to us, the program will lock up (both our python code and hyperterminal will crash when tested). It worked in hyperterminal and inputting it slowly (>.5 seconds between strokes), and would crash when the keyboard was bashed. So what we did was introduce a time.sleep which is longer than .5 seconds, but it is still not working.

Here's our test code.

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)

our receive data function. We used to have the "ok" being sent everytime it recieved a char (which is how we know that it freezes after 3 iterations). We brought it outside of the loop to see if that was causing the problem and it was not the case. It is not sending the "ok" at all with this code.

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

贱贱哒 2024-10-09 21:00:16

(这个答案假设您使用的是 PIC16,根据某些寄存器的名称建议。)

简而言之,它看起来像是缓冲区溢出以及 receiveData 中该循环中的错误。事实上,在短时间内连续发送三个字符后它就冻结了,这可以通过手册的 p117 来解释:

可以接收两个字节的数据并将其传输到 RCREG FIFO,第三个字节开始移位到 RSR 寄存器

这可以解释幻数 3。

单步执行 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 代码中的优先级应该是:从错误中快速恢复并且不锁定。错误检测和正常恢复应该由客户端代码处理,这要容易得多。

(This answer assumes that you are using a PIC16, suggested by the names of certain registers.)

In short, it looks like a buffer overflow coupled with a bug in that loop in receiveData. The fact that it's freezing after three characters are sent in short succession might be explained by p117 of the manual:

It is possible for two bytes of data to be received and transferred to the RCREG FIFO and a third byte to begin shifting to the RSR register

This would explain the magic number three.

Stepping through your PIC code, consider the following scenario (just an example). First time around:

// 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++;

Second time around:

// 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++;

Now the rest of the loop will keep reading from RCREG until index == length, but since some characters were discarded while the UART FIFO was full, you'll never get there and appear to freeze!

What is even more likely is that you are receiving characters before you even get to that function, so the UART FIFO fills up before you even get there.

There are a few ways around this.

  1. Do this in an interrupt so it's a bit faster to move the incoming characters into the buffer.
  2. Use a loop for reading from RCREG: while(RCIF) rxData[index]= RCREG; this makes sure you empty the buffer when reading from the UART buffer, but it will not stop overflows outside of this function or during that delay though.
  3. Check the OERR flag - if it is set, assume something bad happened and start over.
  4. Have a stop character or start character (eg. end-of-line, punctuation, etc) that tells you when a valid command is starting or stopping. If you get two start characters without a stop character, or some other confusing combination, assume you're in a bad state and start over.

Some additional advice: you can go absolutely crazy trying to account and compensate for every missed character or problem like this in your PIC code, but ultimately it's just another comms error. Priorities in the PIC code should be: quick recovery from errors and not locking up. Error detection and sane recovery should be handled by the client code, where it's far, far easier.

执笔绘流年 2024-10-09 21:00:16

Does the communication from the PIC make use of the RTS/CTS lines of the serial port ? Probably the PIC expects some sort of flow control and you are sending data too fast to it without any flow control. Read up on the limitations of the PIC and if needed open the port with flow control enabled.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文