通过mlockall()解决-不能通过串行读取某些值以下的ASCII字符

时间:2019-06-30 03:20:56

标签: serial-port ascii corruption baud-rate

我已经安装了连接到我的linux PC的远程微控制器,该微控制器吐出连续的字符串。我已经使用screen配置了端口(通过发出screen / dev / ttyS0 57600)。

我使用od命令验证了数据,并得到了预期的结果。数据遵循8位十六进制值...

    0006040 03 01 09 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006100 00 00 00 00 00 00 00 00 00 00 00 06 0b a5 00 03
    0006120 01 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006160 00 00 00 00 00 00 00 00 00 00 06 0b a5 00 03 01
    0006200 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006240 00 00 00 00 00 00 00 00 00 06 0b a5 00 02 01 09
    0006260 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006300 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006320 00 00 00 00 00 00 00 00 0f 0c a5 00 02 01 09 00
    0006340 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006360 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006400 00 00 00 00 00 00 00 0f 0c a5 00 02 01 09 00 00
    0006420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006460 00 00 00 00 00 00 0f 0c a5 00 01 01 09 00 00 00
    0006500 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006520 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006540 00 00 00 00 00 05 0c a5 00 01 01 09 00 00 00 00
    0006560 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006600 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006620 00 00 00 00 05 0c a5 00 01 01 09 00 00 00 00 00
    0006640 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006660 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    0006700 00 00 00 05 0c a5 01 08 01 09 00 00 00 00 00 00

然后,我创建一个简单的C程序以尝试读取相同的数据,但是我从C程序中得到的只是以下输出:

  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 a5:1 
  a5:1

基于该输出,似乎read函数确实返回了正在读取一个字节的信息,但是每次返回的值是a5时,即微型计算机发送给PC的标志字节,以指示新的固定字节流尺寸数据。

因此,我的程序正确地覆盖了读取缓冲区中的现有“ A”值,但是它需要始终使用正确的数据进行更新(无论是00还是01),而不仅仅是a5

我在做什么错了?

我在下面附上了完整的代码,供某些人尝试重现该问题时使用。是的,我正确配置了波特,数据位和停止位。

  #include <stdio.h>
  #include <stdlib.h>
  #include <fcntl.h>
  #include <time.h>
  #include <unistd.h>

  #define TIMEOUT 5

  int main(){
    char inb[3];  //our byte buffer
    int nread=0;  //number bytes read from port
    int n;        //counter
    int iosz=128; //Lets get 128 bytes
    int fd=open("/dev/ttyS0",O_NOCTTY | O_RDONLY | O_SYNC); //Open port
    for(n=0;n<iosz;n++){
      int s=time(NULL); //Start timer for 5 seconds
      while (time(NULL)-s < TIMEOUT && nread < 1){
        inb[0]='A'; //Fill buffer with bad data
        inb[1]='B';
        inb[2]='C';
        nread=read(fd,inb,1); //Read ONE byte
        if (nread < 0 || time(NULL)-s >= TIMEOUT){
            close(fd); //Exit if read error or timeout
            return -1;
        }
      }
      printf("%x:%d ",inb[0] & 0xFF,nread); //Print byte as we receive it
    }
    close(fd); //program ends so close and exit
    return 0;
  }

2 个答案:

答案 0 :(得分:0)

  

无法通过串行读取具有特定值的Ascii字符

这是对程序缺陷的虚假分析。
偶然发现,收到的最大值似乎是0xA5,小的样本转储似乎总是在该字节之前带有0x0B和0x0C。
如果转储要显示更多数据,最终在字节0xA5之前是否会有0x0D(回车)?

因此,如果规范模式处于活动状态(通常是默认模式),并且0x0D(即行定界符)位于其前面,则0xA5将是行的第一个字符。
由于您的程序一次只请求一个字符,因此规范的 read()仅返回该行的第一个字符。

  

我在做什么错?

您的程序有两个重要的错误。

首先,您的程序忽略了配置串行终端的功能,因此您不知道程序使用的是规范模式还是非规范模式。

第二,您的程序仅重复打印单个 read()的相同结果。
您仅读取某些值的分析是不准确的,因为实际上仅打印了单个 read()的结果。
printf()不在 while 循环(执行读取)的范围内,因此您的程序不会打印每个 read()的内容>已获得,但仅打印成功 read()获得的 first
在第一个成功的 read()之后,变量nread的值为1,并且 while 循环中的nread < 1比较将始终评估为FALSE。
因此,在第一个成功读取之后,不再出现 read(),但是由于 for 循环,将重复打印第一个读取的结果。

因此,如果在运行程序时非规范模式处于活动状态,而'0xA5'恰好是第一个 read(),那么这将是打印的次数(很多次)根据您的程序。


由于接收到的数据是二进制而不是ASCII文本,因此您需要将串行终端配置为非规范(也称为原始)模式。
然后,您可以坚持低效率地一次读取一个字节。

答案 1 :(得分:-1)

我终于知道了!

经过几天的脑筋急转弯,我找到了最终的答案。

是的,我已经实现了在串行端口上一次同时具有更多字节而不是一次一个字节的I / O,但这并不能解决问题。

从字面上解决问题的东西有两个:

  1. 由于串行时序很严格(56k时每173uS 1个字符),因此我需要使该程序具有较高的优先级。为此,我在主要功能的开头添加了此代码:

    const struct sched_pa​​ram schedp = {99}; sched_setscheduler(0,SCHED_FIFO,&schedp);

,我也将其添加到了包含内容中

#include <sched.h>

这使程序具有优先权,但是令人惊讶的是,最重要的是内存锁定围绕着需要关键的部分,而那些关键部分正在/向/读取。端口。

    mlockall(MCL_CURRENT);  
    //use read() or write() in here with file descriptor as serial port
    munlockall();

,我也将其添加到了包含内容中

#include <sys/mman.h>

我考虑过使用MCL_FUTURE作为参数,但这意味着要求锁定内存中的更多资源,但是在发现不必要的资源之后,我将使用MCL_CURRENT。

我不在乎系统上的其余进程是否锁定了5秒钟,但至少现在,数据I / O在串行端口上的运行状况提高了100%。