【问题标题】:Reading serial port with initial data results in lots of zeros用初始数据读取串行端口会导致很多零
【发布时间】:2014-06-03 02:39:44
【问题描述】:

我正在测试一个串行实现,当我打开一个串行端口时(我有一个 Arduino 吐出罗盘数据行。)有时我最初会得到一堆零。我以为这是之前的剩余数据,但似乎不是(刷新 IO 似乎没有帮助)

这是一个用 C 编写的编程语言串行实现,我正在 Linux 上进行测试,但在 Windows 上得到了类似的结果。

strace 输出在第一次读取时显示:

read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 34815) = 1544
write(1, "== ", 3== )                      = 3
write(1, "#{\n00000000000000000000000000000"..., 503#{
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000583A20353333202020593A20313020
20205A3A2033313920202058733A2033393520202059733A203136312020205A
733A20323933202020483A20302E3436202020413A...) = 503

我尝试在端口关闭之前、端口打开之后以及设置的属性之后添加以下行来清除这些数据:

tcflush(ttyfd, TCIOFLUSH); 

但它似乎对解决这个问题没有帮助。关于如何清理它的任何想法?

代码来自一个大型项目,我收集了下面的一些相关部分,变量声明即使没有显示但应该足够清楚

打开端口:

ttyfd = open(&devpath[0], O_RDWR | O_NOCTTY | O_NONBLOCK); // ttyUSB0 in this case

更改设置:

    if (speeds[n] == 0) speed = B115200; // invalid, use default

    cfsetospeed (&attr, speed);
    cfsetispeed (&attr, speed);

    // C-flags - control modes:
    attr.c_cflag |= CREAD | CS8 | CLOCAL;

    // L-flags - local modes:
    attr.c_lflag = 0; // raw, not ICANON

    // I-flags - input modes:
    attr.c_iflag |= IGNPAR;

    // O-flags - output modes:
    attr.c_oflag = 0;

    // Control characters:
    // device is non-blocking (polled for changes):
    attr.c_cc[VMIN]  = 0;
    attr.c_cc[VTIME] = 0;

    // Make sure OS queues are empty:
    tcflush(ttyfd, TCIOFLUSH);

    // Set new attributes:
    if (tcsetattr(ttyfd, TCSANOW, &attr)) return 2;

这是从 OSEPP Compass 模块发送数据的 Arduino 代码

// OSEPP Compass Sensor Example Sketch
// by OSEPP <http://www.osepp.com>
// Modifications by Chris W. to accommodate declination, scaling and origin adjustment 2013-02-13
// This sketch demonstrates interactions with the Compass Sensor

#include <Wire.h>

const uint8_t sensorAddr = 0x1E;   // Sensor address (non-configurable)
const float xOffset = 103.0;      // Offset required to adjust x coordinate to zero origin
const float yOffset = -165.0;       // Offset required to adjust y coordinate to zero origin
const float declination = 70.1;    // Enter magnetic declination mrads here (local to your geo area) 

// One-time setup
void setup()
{
   // Start the serial port for output
   Serial.begin(115200);

   // Join the I2C bus as master
   Wire.begin();

   // Configure the compass to default values (see datasheet for details)
   WriteByte(sensorAddr, 0x0, 0x70);

   WriteByte(sensorAddr, 0x1, 0x20); // +1.3Ga

   // Set compass to continuous-measurement mode (default is single shot)
   WriteByte(sensorAddr, 0x2, 0x0);
}

// Main program loop
void loop()
{
   uint8_t x_msb;   // X-axis most significant byte
   uint8_t x_lsb;   // X-axis least significant byte
   uint8_t y_msb;   // Y-axis most significant byte
   uint8_t y_lsb;   // Y-axis least significant byte
   uint8_t z_msb;   // Z-axis most significant byte
   uint8_t z_lsb;   // Z-axis least significant byte

   int x;
   int y;
   int z;

   // Get the value from the sensor
    if ((ReadByte(sensorAddr, 0x3, &x_msb) == 0) &&
       (ReadByte(sensorAddr, 0x4, &x_lsb) == 0) &&
       (ReadByte(sensorAddr, 0x5, &z_msb) == 0) &&
       (ReadByte(sensorAddr, 0x6, &z_lsb) == 0) &&
       (ReadByte(sensorAddr, 0x7, &y_msb) == 0) &&
       (ReadByte(sensorAddr, 0x8, &y_lsb) == 0))
    {
    x = x_msb << 8 | x_lsb;
    y = y_msb << 8 | y_lsb;
    z = z_msb << 8 | z_lsb;

    int xs;
    int ys;
    int zs;

    float gScale = .92;  // Scale factor for +1.3Ga setting

    float adjx = x - xOffset;
    float adjy = y - yOffset;

    xs = adjx * gScale; 
    ys = adjy * gScale;
    zs = z * gScale;    

    float heading = atan2(ys, xs);
    heading += declination / 1000; // Declination for geo area

      if (heading < 0);
        heading += 2*PI;

      if (heading > 2*PI)
        heading -= 2*PI;

      float angle = heading * 180/M_PI;

      Serial.print("X: ");
      Serial.print(x);
      Serial.print("   Y: ");
      Serial.print(y);
      Serial.print("   Z: ");
      Serial.print(z);
      Serial.print("   Xs: ");
      Serial.print(xs);
      Serial.print("   Ys: ");
      Serial.print(ys);
      Serial.print("   Zs: ");
      Serial.print(zs);
      Serial.print("   H: ");
      Serial.print(heading);
      Serial.print("   A: ");
      Serial.println(angle);
   }
   else
   {
      Serial.println("Failed to read from sensor");
   }

   // Run again in 1 s (1000 ms)
   delay(500);
}

// Read a byte on the i2c interface
int ReadByte(uint8_t addr, uint8_t reg, uint8_t *data)
{
   // Do an i2c write to set the register that we want to read from
   Wire.beginTransmission(addr);
   Wire.write(reg);
   Wire.endTransmission();

   // Read a byte from the device
   Wire.requestFrom(addr, (uint8_t)1);
   if (Wire.available())
   {
      *data = Wire.read();
   }
   else
   {
      // Read nothing back
      return -1;
   }

   return 0;
}

// Write a byte on the i2c interface
void WriteByte(uint8_t addr, uint8_t reg, byte data)
{
   // Begin the write sequence
   Wire.beginTransmission(addr);

   // First byte is to set the register pointer
   Wire.write(reg);

   // Write the data byte
   Wire.write(data);

   // End the write sequence; bytes are actually transmitted now
   Wire.endTransmission();
}

我将尝试在 arduino 代码中切换延迟和串行写入的顺序,因为它可能会解决此问题,但对于类似的未来场景,该代码将仍然无效。

【问题讨论】:

  • 没有看到实际代码,尤其是 RX 和 TX 中断处理程序,看起来中断正在触发和读/写字符,即使没有可用的字符。
  • 添加了部分代码
  • 假设您的程序对读取缓冲区的处理是正常的,我想说当 Andrino 发送时,它负责接收到的内容。
  • 在 tcsetattr 之后执行 tcflush。
  • @alk 感谢您的评论。仍在调查中。

标签: c serial-port posix


【解决方案1】:

查看了 IC 的数据表。

见第 14 页:Link

连续测量模式。在连续测量模式下,设备连续执行测量并将结果存入数据寄存器。 当新数据放入所有三个寄存器时,RDY 变为高电平上电或写入模式或配置寄存器后,第一个测量集在 2/f_DO 周期后可从所有三个数据输出寄存器中使用,随后的测量以 f_DO 的频率提供,其中 f_DO是数据输出的频率

根据 Arduino 文档,Wire 库以 100KHz 运行。 2/100KHz == 20uS。假设您的 Arduino 以 16MHz(1 个周期 == 62.5ns)运行,您可能没有等待足够长的时间来读取寄存器。

为了使您的代码更健壮,我建议在执行任何读取之前检查状态寄存器的 RDY 位。数据可能是正确的,但您必须考虑 IC 的启动时间。

如果您这样做,并且发现同样的问题仍然存在,那么至少您将进一步隔离问题。


或者:如果您只想快速破解,可以将 500 毫秒延迟放在主循环的底部。

【讨论】:

  • 还没来得及确认这一点,但我相信这可能是原因。谢谢!
猜你喜欢
  • 2013-02-12
  • 2016-01-11
  • 2020-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-14
相关资源
最近更新 更多