是什么让嵌入式设备上的 RS232 串行通信方案变得强大?

发布于 2024-10-14 23:26:21 字数 1856 浏览 5 评论 0原文

因此,我在编写串行通信方案时开始遇到重复出现的问题,这似乎是由于时间原因造成的。我的嵌入式平台是 Rabbit Semiconductor BL2600,在本例中,它正在与 RS232 上的设备通信。它向设备发送命令,设备将回复发送回 BL2600,然后进行处理。

但我的问题是,当我发出命令,然后等待响应时,我没有得到响应。但是,如果我在正确的位置设置断点,并单步执行代码,我通常会得到响应。我将我的计算机放在BL2600和监听RS232流的设备之间(在看到最初的问题之后),无论我是否断点,回复都会发送,但BL2600只能在其缓冲区中看到它如果我在解析代码的部分之前停下来并尝试找到它的起始位。如果我在读取整个字符串时断点,它将找不到它。

所以,这听起来好像我没有等待足够的时间,所以现在只是为了荒谬,我已将检查缓冲区的超时设置为 1 秒(并且波特率为 38400,它最好显示在该窗口中),但除非我断点并单步执行,否则我仍然一无所获。

下面是我的代码的重要部分:

    //clear the buffers
 serCwrFlush();
 while(serCwrUsed())
 {
  ;
 }
 startwait = MS_TIMER;
 while((serCrdUsed() > 1) && (device_timeout_check < 1000))
 {
  if (MS_TIMER < startwait)
  {                                    // fix the rollover
   device_timeout_check = MS_TIMER + (ULONG_MAX - startwait);  
  }

  else
  {                                   //set it like normal
   device_timeout_check = MS_TIMER - startwait;     
  }

  serCrdFlush();
 }
 serCputs("mpcal=d\r");     //This is what requests the response from the device
 while(serCwrUsed())
 {
  ;
 }
 startwait = MS_TIMER;
 while((serCrdUsed() < 11) && (device_timeout_check < 1000))
 {
  if (MS_TIMER < startwait)
  {                        // fix the rollover
   device_timeout_check = MS_TIMER + (ULONG_MAX - startwait);  
  }
  else
  {                       //set it like normal
   device_timeout_check = MS_TIMER - startwait;     
  }
 }
 //grab it
 temp=serCpeek();
 i=0;
        //It expects a response like "H0V0M00.0 /r"
        //So I am looking for the first character.
 while((temp != 'H' ) && (i<100))
 {
  serCgetc();     //breakpoint works here
  temp=serCpeek();
  i++;
 }
 c=serCread(comp_cal_string,20, 20);   //breakpoint doesn't work here

我有一种感觉,我正在重新发明轮子,并且有人可能在我之前完成了这件事,至少在另一个平台上,因此它被延迟了足够长的时间,以便数据被已收到,但速度足够快,足以实际捕获数据。

So I'm starting to have a reoccurring problem with writing serial communication schemes, and it seems to be due to timing. My embedded platform is the Rabbit Semiconductor BL2600, and in this case, it is talking to a device on RS232. It sends out a command to the device, and the device sends a reply back to the BL2600, which is then processed.

My issue is though, that when I send out the command, and then wait for a response, I don't get one. However if I set a breakpoint at the right spots, and single step through the code, I often do get the response. I have placed my computer in between the BL2600 and the device to listen in to the RS232 stream (after seeing the initial problem), and the reply is being sent no matter if I breakpoint or not, but the BL2600 only sees it in its buffers if I stop just before the part where I parse the code and try to find its start bit. If I breakpoint when its just reading the the whole string afterwards, it won't find it.

So, that just sounds like I'm not waiting enough, so now just to be ridiculous, I have set the timeouts for checking the buffer up to 1 second (and with a baud rate of 38400, it had better show up in that window), and yet I STILL get nothing unless I breakpoint and single-step.

Below is the significant part of my code:

    //clear the buffers
 serCwrFlush();
 while(serCwrUsed())
 {
  ;
 }
 startwait = MS_TIMER;
 while((serCrdUsed() > 1) && (device_timeout_check < 1000))
 {
  if (MS_TIMER < startwait)
  {                                    // fix the rollover
   device_timeout_check = MS_TIMER + (ULONG_MAX - startwait);  
  }

  else
  {                                   //set it like normal
   device_timeout_check = MS_TIMER - startwait;     
  }

  serCrdFlush();
 }
 serCputs("mpcal=d\r");     //This is what requests the response from the device
 while(serCwrUsed())
 {
  ;
 }
 startwait = MS_TIMER;
 while((serCrdUsed() < 11) && (device_timeout_check < 1000))
 {
  if (MS_TIMER < startwait)
  {                        // fix the rollover
   device_timeout_check = MS_TIMER + (ULONG_MAX - startwait);  
  }
  else
  {                       //set it like normal
   device_timeout_check = MS_TIMER - startwait;     
  }
 }
 //grab it
 temp=serCpeek();
 i=0;
        //It expects a response like "H0V0M00.0 /r"
        //So I am looking for the first character.
 while((temp != 'H' ) && (i<100))
 {
  serCgetc();     //breakpoint works here
  temp=serCpeek();
  i++;
 }
 c=serCread(comp_cal_string,20, 20);   //breakpoint doesn't work here

I have a feeling that I am re-inventing the wheel, and that somebody has probably done this before me, at least on another platform, so that it is delayed long enough for the data to be received, but fast enough that it actually catches the data.

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

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

发布评论

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

评论(3

蛮可爱 2024-10-21 23:26:21

您不使用中断驱动的 RS-232 通信有什么原因吗?通常发生的情况是在 RS-232 电路的边沿转换时触发中断,并且您设置一个中断服务例程 (ISR) 来处理它 - 这样,您总是可以处理信息,因为中断是硬件-驱动。来自 RS-232 电路的脉冲可能足够短,以至于您的轮询循环(上面)根本看不到它。

如果您的 BL2600 与此处所述的设备相同:

http://ftp1.digi。 com/support/documentation/019-0113_N.pdf

该手册列出了设备上所有四个串行端口的中断向量 - 请参阅第 476 页的 setVectIntern。

此外,较旧的 RS-232 电路仅支持 16 字节的内部缓冲区。如果您希望提高波特率,您可能需要使用 BL2600 上提供的 DMA 功能。 (DMA 功能应自动将大数据块从内存传输到串行端口,反之亦然。)

Is there a reason you're not using interrupt-driven RS-232 communications? Usually what happens is an interrupt is triggered on an edge transition from the RS-232 circuitry, and you set up an interrupt service routine (ISR) to handle it - that way, you ALWAYS get to process the information, because the interrupt is hardware-driven. The pulse from the RS-232 circuitry may be short enough that your polling loop (above) is just not seeing it.

If your BL2600 is the same device as described here:

http://ftp1.digi.com/support/documentation/019-0113_N.pdf

That manual lists interrupt vectors for all four serial ports on the device - see setVectIntern on page 476.

Also, older RS-232 circuitry only supported internal buffers of 16 bytes. If you're looking to crank up the baud rate, you may need to use the DMA features that seem to be present on the BL2600. (The DMA features should automate the transfer of large blocks of data from memory to serial port and vice versa.)

初懵 2024-10-21 23:26:21

稳健性的关键是错误处理。

有两个循环可能会超时,但发生超时时没有任何不同。有些函数的返回值未进行错误检查。不处理错误的代码永远不会健壮。

超时循环以三种方式中断,可能会影响您的阅读:

  • device_timeout_check 的值在循环内设置其值之前进行测试。
  • MS_TIMER 是不稳定的,在测试和用于计算 device_timeout_check 的时间之间可能会发生变化。
  • 发生超时时不会执行任何操作。

要解决这些问题,首先更改 startwait 的声明以避免翻转问题:
无符号长启动等待;。然后像这样实现超时循环:

startwait = MS_TIMER;
do {
  device_timeout_check = (MS_TIMER - startwait >= 1000);
} while((serCrdUsed() > 1) && !device_timeout_check);

if (device_timeout_check) {
   /* Handle the timeout error here */
}

当到达您的代码时,如果 device_timeout_check 大于 999,这将解决问题。

尝试检测第一个“H”字符的循环还存在另一个问题。前面的代码仅确保有 11 个字符可用。检测循环运行 100 次,但不允许有额外的时间来接收字符。如果缓冲区中已经有 11 个或更多非“H”字符,它们将立即被消耗,并等待“H”。

最好是先等‘H’,再等十一个字符。如果所需输入之前有不需要的输入,这将解决问题。

The key to robustness is error handling.

There are two loops that can time out, but neither does anything different when a timeout occurs. There are calls to functions whose return values are not checked for errors. Code that doesn't handle errors is never robust.

The timeout loops are broken in three ways that could affect your reading:

  • The value of device_timeout_check is tested before its value is set inside the loop.
  • MS_TIMER is volatile and can change between the time it is tested and used to calculate device_timeout_check.
  • Nothing is done when a timeout occurs.

To fix these problems, first change the declaration of startwait to avoid rollover problems:
unsigned long startwait;. Then implement the timeout loops like this:

startwait = MS_TIMER;
do {
  device_timeout_check = (MS_TIMER - startwait >= 1000);
} while((serCrdUsed() > 1) && !device_timeout_check);

if (device_timeout_check) {
   /* Handle the timeout error here */
}

This will solve the problem if device_timeout_check is greater than 999 when your code is reached.

There is another problem with the loop trying to detect the first 'H' character. The code prior to it only ensures that there are 11 characters available. The detection loop runs 100 times, but allows no extra time for characters to be received. If there are already 11 or more non-'H' characters in the buffer, they will be consumed immediately and there will be waiting for the 'H'.

It would be better to wait for the 'H' first and then wait for the eleven characters. This will solve the problem if the desired input is preceded by undesired input.

又爬满兰若 2024-10-21 23:26:21

我认为丁戈的第一个发现给出了正确的答案。 device_timeout_check 未初始化,至少在此处提供的代码中是这样。如果没有初始化,它只是调用堆栈上留下的垃圾,所以它很容易超过 1000。在这种情况下,while 循环会立即静默地失败(tsk tsk),而等待“H”的 while 循环将快速运行 100 次迭代,没有从串行端口拉出任何内容(很可能)。

顺便说一句,为什么是 100?一旦清空缓冲区,这些额外的读取很可能会发生得如此之快,以至于它们不会找到任何新的东西。

这也完美地解释了调试器的行为。你的延迟循环从来没有真正延迟任何东西,当你在 serCgetc() 处放置断点时,你允许串行端口赶上程序。

查看情况是否确实如此的一个简单方法是在尝试从串行端口读取数据之前设置某种外部通知。使 LED 闪烁或通过串行端口发送“已收到消息”数据包。然后,使用便宜的 O 型示波器,您应该可以轻松地看到在接收数据之前发送的“消息已接收”数据包(或 LED 闪烁)。

这些查看和读取函数不会返回错误状态,因为它们返回数据,但它们可以轻松地放置在包装器中,至少检查 FIFO 中的奇偶校验错误或字节,并在指针中返回数据,以便可以返回状态。像这样

STATUS myCpeek( unsigned char *data)
{
  if ( 0 == serCrdUsed() )
    return ERROR_NO_DATA;
  *data = serCpeek();
  if ( serCParityError() )  //Just guessing at a function name here
  {
    return ERROR_PARITY_BAD;
  }
  return OK;
}

或类似的东西。

I think Dingo has the answer correct with his first finding. device_timeout_check is not initialized, at least in the code presented here. If it's not initialized, it's just whatever garbage was left on the call stack, so it could be easily over 1000. In that case, the while loops immediately fall through silently (tsk tsk), and the while loop waiting for 'H' will rapidly run through 100 iterations pulling nothing (most likely) from the serial port.

BTW, why 100? Once you empty the buffer, those extra reads are most likely going to occur so fast that they are not going to find anything new.

This also perfectly explains the behavior with the debugger. Your delay loops were never really delaying anything, and when you put a breakpoint at serCgetc(), you were allowing for the serial port to catch up with the program.

An easy way to see if this is really the case is to setup some kind of external notification before you attempt to read the data from the serial port. Either blink an LED or send a "message received" packet across the serial port. Then with a cheap O-scope you should easily see the "message received" packet (or LED blink) be transmitted before the reception of the data.

Those peek and read functions don't return error status since they return data, but they could easily be placed in wrappers that at least checked for parity errors or bytes in the FIFO, and returned data in a pointer instead so status could be returned. Something like this

STATUS myCpeek( unsigned char *data)
{
  if ( 0 == serCrdUsed() )
    return ERROR_NO_DATA;
  *data = serCpeek();
  if ( serCParityError() )  //Just guessing at a function name here
  {
    return ERROR_PARITY_BAD;
  }
  return OK;
}

or something similar.

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