Почему последовательный порт пропускает данные при отправке данных?

Я написал некоторый код C ++ для общения с моим Arduino через последовательный порт. Он просто пытается совершить колебания на двух серводвигателях, используя синус и косинус, но пропускает данные. Я не уверен, почему это происходит. Я использую termios.h для серийного материала. Вывод из C ++ выглядит примерно так: «V180H90», то есть Vertical 180, Horizontal 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;
}

2

Решение

Цикл «processIncomingByte» внутри устройства может испытывать проблему со скоростью, поскольку вы обрабатываете предыдущее состояние (handlePreviousState) сразу после получения нового режима.

Проблема может быть вызвана выполнением Serial.print в соответствующей функции, в то время как байты значения-данных все еще непрерывно поступают с ПК. Последовательная печать является относительно медленным процессом в логике микроконтроллера.

Я не знаком с аппаратным обеспечением Arduino, но некоторые платы микроконтроллеров нижнего уровня выполняют программный последовательный интерфейс с использованием метода битовой синхронизации, поэтому при передаче прием полностью прекращается. Чтобы проверить это, вы можете отметить Serial.print, чтобы увидеть, помогает ли он.

В любом случае, выполнение длительной обработки в середине входящего потока данных всегда проблематично, если только у вас нет аппаратного последовательного интерфейса в устройстве с большим количеством буферов FIFO.

Надлежащим способом решения этой проблемы является получение всего сообщения внутри буфера, а затем обработка его только при получении маркера конца сообщения. Например, вставьте ваше сообщение в пару [], например [V180H90]. Сбросьте буфер после «[» и обработайте буфер после получения «]». Когда вы собираете байты в буфер, убедитесь, что вы также проверяете переполнение буфера.

2

Другие решения

Если вы просто вытолкнете данные в горло порта, он сделает все возможное, чтобы не поджечь, но лишние данные отправлять не будут. В конце концов, порт работает на конечной скорости и является довольно ограниченным и дамповым устройством.

Итак, перед отправкой символа в порт необходимо проверить состояние порта, чтобы увидеть, действительно ли он готов принять другой символ данных для передачи. Некоторые последовательные порты могут даже генерировать прерывания, когда они могут принимать больше данных, чтобы помочь вам избежать бесполезного опроса статуса.

Кроме того, иногда два последовательных порта на двух устройствах могут быть связаны с дополнительной парой сигналов без данных (RTS а также CTS), чтобы указать, готова ли принимающая сторона принять больше данных. Если у вас есть те, кто подключен, и ваше устройство использует их, чтобы указать его готовность, ваша программа должна принять состояние устройства CTS во внимание также.

1

Очевидно, ваше устройство читает / обрабатывает данные медленнее, чем вы отправляете его через последовательный порт. Я вижу несколько возможных решений здесь:

1) Реализация управления потоком и отправки данных через последовательный порт в режиме блокировки. Вам все равно придется ждать после отправки, но только столько, сколько необходимо вашему устройству для чтения и обработки данных.

2) Реализуйте двустороннюю связь, чтобы ваше устройство отправляло подтверждающее сообщение (т. Е. Любой отдельный символ ASCII), чтобы указать, что оно готово принять данные.

3) Разделите ваш код на две параллельные части, то есть: основной цикл (или ISR) считывает только данные с последовательного порта и сохраняет их в кольцевой буфер, другой цикл опрашивает кольцевой буфер и получает / обрабатывает данные из него, как только становятся доступными некоторые данные. Это наиболее сложное решение из трех, поскольку вам нужны два отдельных потока (или поток и ISR) и защита кольцевого буфера от одновременного доступа, но также и самый мощный и гибкий.

1

Вы слишком быстро записываете данные на последовательное устройство, и само устройство выплевывает данные быстрее, чем вы можете прочитать их на другой стороне устройства.

Правильный способ справиться с этим — снизить скорость записи на последовательное устройство, чтобы избежать его переполнения данными.

0
По вопросам рекламы [email protected]