2

我已经编写了一些 C++ 代码来通过串行与我的 arduino 通信。它只是尝试使用正弦和余弦在两个伺服电机上产生振荡,但它正在跳过数据。我不确定为什么会这样。我将 termios.h 用于串行内容。C++ 的输出类似于“V180H90”,即垂直 180,水平 90。我之前使用 fstream 和 usleep() 发送数据并且它正在工作,但我想使用比延迟一些任意数字更好的方法.

感谢您的任何帮助或指导。

我的arduino代码

#include <Servo.h>
typedef enum { NONE, GOT_V, GOT_H } states;
states state = NONE;
Servo pan;
Servo tilt;
int laser = 11;
unsigned int currentValue;

int v_pan = 0;
int v_tilt = 0;

void setup()
{
  pan.attach(10);
  tilt.attach(9);

  Serial.begin(9600);
  state = NONE;
}

void processVertical(const unsigned int value)
{
  Serial.print("Vertical = ");
  Serial.println(value);
  int result = 1300 + (value - 90) * 2;
  //Serial.println(result);
  tilt.writeMicroseconds(result);
}

void processHorizontal(const unsigned int value)
{
  Serial.print("Horizontal = ");
  Serial.println(value);
  int result = 1500 + (value - 180) * 1;
  //Serial.println(result);
  pan.writeMicroseconds(result);
}

void handlePreviousState()
{
  switch(state)
  {
    case GOT_V:
      processVertical(currentValue);
      break;
    case GOT_H:
      processHorizontal(currentValue);
      break;
  }
  currentValue = 0;
}

void processIncomingByte (const byte c)
{
  if (isdigit(c))
  {
    currentValue *=10;
    currentValue += c - '0';
  }
  else
  {
    handlePreviousState();

    switch (c)
    {
      case 'V':
        state = GOT_V;
        break;
      case 'H':
        state = GOT_H;
        break;
      default:
        state = NONE;
        break;
    }
  }
}

void loop()
{
  if(Serial.available() > 0) 
  { 
    processIncomingByte(Serial.read());
  } 
  digitalWrite(laser, HIGH);
}

//check out writeMicroseconds

我的 C++ 代码

// Program for sending data to serial

#include <iostream>
#include <sstream>
#include <string>
#include <termios.h>
#include <fcntl.h>
#include <math.h>

using namespace std;

//open serial port
int openPort(string path)
{
  int fd; //file descriptor for port
  fd = open(path.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
  if (fd == -1)
    cerr << "Cannot open port" << endl;
  else
    fcntl(fd, F_SETFL, 0);
  return (fd);
}

//set options for an open serial port
void setOptions(int fd)
{
  struct termios options;
  tcgetattr(fd, &options);
  cfsetispeed(&options, B9600);
  cfsetospeed(&options, B9600);

  //No parity 8N1
  options.c_cflag &= ~PARENB;
  options.c_cflag &= ~CSTOPB;
  options.c_cflag &= ~CSIZE;
  options.c_cflag |= CS8;

  //No flow control
  options.c_cflag &= ~CRTSCTS;

  //Turn off s/w flow control
  options.c_iflag &= ~(IXON | IXOFF | IXANY);

  //Turn on read and ignore ctrl lines
  options.c_cflag |= (CLOCAL | CREAD); 

  if( tcsetattr(fd, TCSANOW, &options) < 0) { 
    cerr << "Could not set attributes" << endl; 
  }
}

//write to serial port
void writePort(int fd, string data)
{
  int n = write(fd, data.c_str(), 9);
  if (n < 0)
    cerr << "Cannot write to port" << endl;
}

int main() {
  string path = "/dev/tty.usbmodemfd131";
  //string path = "/dev/tty.usbmodemfa141";
  int fd = openPort(path);
  setOptions(fd);

  stringstream ss;
  string output;
  unsigned short vertical = 0;
  unsigned short horizontal = 0; 
  unsigned short freq = 10;

  for(int i = 0; i < 360; i++) {
    vertical = ((cos(i * freq * ((M_PI)/180))) + 1) * 90;
    horizontal = ((sin(i * freq * ((M_PI)/180))) + 1) * 90;
    ss << "V" << vertical << "H" << horizontal << endl; 
    output = ss.str();
    ss.str("");
    writePort(fd, output);
//    cout << output; //DEBUG
  }

  close(fd);
  return 0;
}
4

4 回答 4

2

设备内部的“processIncomingByte”循环可能遇到了速度问题,因为您在收到新模式后立即处理先前的状态(handlePreviousState)。

该问题可能是由于在相应函数中执行 Serial.print 而导致值数据字节仍连续从 PC 传入。串行打印在微控制器逻辑中是一个相对较慢的过程。

我对Arduino硬件不熟悉,但是一些低端的微控制器板正在使用bitbanging方法执行软件串行接口,所以当你发送时,接收完全停止。要验证这一点,您可以注释 Serial.print 以查看它是否有帮助。

无论如何,在传入数据流的中间进行冗长的处理总是有问题的,除非您在设备中有一个具有大量 FIFO 缓冲区的硬件串行接口。

解决此问题的正确方法是首先在缓冲区中接收整个消息,然后仅在收到消息结束标记时处理它。例如,将您的消息插入到 [] 对中,如 [V180H90]。在“[”上重置缓冲区并在收到“]”后处理缓冲区。当您将字节收集到缓冲区中时,请确保您还检查缓冲区溢出。

于 2013-02-05T14:34:33.053 回答
1

显然,您的设备读取/处理数据的速度比通过串行端口发送数据的速度要慢。我在这里看到几个可能的解决方案:

1) 实现流控,以阻塞方式通过串口发送数据。发送后您仍然需要等待,但只需等待设备读取和处理数据所需的时间即可。

2) 实现双向通信,以便您的设备发送确认消息(即任何单个 ASCII 符号)以表明它已准备好接受数据。

3)将您的代码分为两个并行部分,即:主循环(或ISR)仅从串行端口读取数据并将其存储在环形缓冲区中,另一个循环轮询环形缓冲区并在有数据时立即从中获取/处理数据一些可用的数据。这是三者中最困难的解决方案,因为您需要两个单独的线程(或一个线程和一个 ISR)并保护环形缓冲区免受并发访问,但也是最强大和最灵活的。

于 2013-02-05T07:01:56.147 回答
1

If you just shove data down the port's throat, it'll do its best not to set on fire, but the excess data isn't going to be sent. After all, the port operates at a finite speed and is a pretty limited and dump device.

So, before sending a character to the port you need to check the status of the port to see if it's actually ready to accept another character of data for transmission. Some serial ports can even generate interrupts when they can take more data to help you avoid wasteful status polling.

Also, sometimes two serial ports on the two devices can be connected with an extra pair of non-data signals (RTS and CTS) to indicate whether the receiving side is ready to receive more data. If you have those connected and your device is using them to indicate its readiness, your program should take the state of the device's CTS into account as well.

于 2013-02-05T06:49:59.903 回答
0

您将数据写入串行设备的速度太快,并且设备本身吐出数据的速度比您在设备另一侧读回数据的速度要快。

解决这个问题的正确方法是限制对串行设备的写入速度,以避免数据泛滥。

于 2013-02-05T06:19:25.443 回答