C: Нет связи с подключенным последовательным портом?

Я пытаюсь подключиться к устройству Arduino Mega At2560, подключенному через последовательный порт USB, на моем ПК с ОС Linux. Используя код C, я пытаюсь отправлять и получать простые текстовые строки, просто я могу отправлять и получать обе стороны.

На ардуино:

int incomingByte = 0;    // для входящих последовательных данных

void setup() {
    Serial.begin(19200);    // открывает последовательный порт, устанавливает скорость передачи данных 9600 бит/с
}

void loop() {
  // отправлять данные только при получении данных:
  if (Serial.available() > 0) {

    // прочитать входящий байт:
    incomingByte = Serial.read();

    // скажите, что у вас есть:
    Serial.print((char)incomingByte);
  }

}

по сути, это просто цикл, который проверяет, есть ли последовательные данные, и если да, то считывает их и печатает обратно. есть (символьное) преобразование, поэтому я сразу увижу, что отправленные данные - это то, что я получил обратно (сторона Linux)

Для кода Linux я использую довольно стандартный код для открытия порта, который я нашел здесь

Я называю Arduino «столом», так как в конечном итоге он будет управлять движущимся столом с помощью команд USB.

C-файл:

#include "TableFunctions.h"

bool connected=false;
int fd;
char *portname;


int set_interface_attribs (int fd, int speed, int parity)
{
        connected=false;
        struct termios tty;
        struct termios tty_old;
        memset(&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                printf("PC: Error %d from tcgetattr  \n", errno);
                return -1;
        }
        tty_old = tty;

        cfsetospeed (&tty, (speed_t)B19200);
        cfsetispeed (&tty, (speed_t)B19200);

        //tty.c_cflag |= B19200;
        tty.c_cflag     &=  ~PARENB;            // Делаем 8n1
        tty.c_cflag     &=  ~CSTOPB;
        tty.c_cflag     &=  ~CSIZE;
        tty.c_cflag     |=  CS8;

        tty.c_cflag     &=  ~CRTSCTS;           // без управления потоком
        tty.c_cc[VMIN]   =  1;                  // чтение не блокируется
        tty.c_cc[VTIME]  =  5;                  //
        tty.c_cflag     |=  CREAD | CLOCAL;     // включаем READ & игнорировать строки ctrl
        cfmakeraw(&tty);
        tcflush( fd, TCIFLUSH );

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
        {
                printf("PC: Error %d from tcsetattr  \n", errno);
                return -1;
        }
        return 0;
}

void set_blocking(int fd, int should_block)
{
        struct termios tty;
        memset(&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                printf("PC: Error %d from tggetattr  \n", errno);
                return;
        }

        tty.c_cc[VMIN]  = should_block ? 1 : 0;
        tty.c_cc[VTIME] = 10;            // тайм-аут чтения 0,5 секунды

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
                printf("PC: Error %d setting term attributes  \n", errno);
}


void OpenSerialPort()
{
    char *portname = "/dev/ttyACM0";

    printf("PC: Opening port to table \n");

    int fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);

    usleep(2000000);
    if (fd < 0)
    {
        printf("PC: Error %d opening %s: %s  \n", errno, portname, strerror(errno));
        return;
    }

    set_interface_attribs(fd, B19200, 0);  // устанавливаем скорость 19 200 бит/с, 8n1 (без четности)
    set_blocking(fd, 0);                // не ставим блокировку
    printf("PC: Connected\n");
    connected = true;
}
void PrepareWriteCommand(int numberOfCommands, const char *commands[numberOfCommands])
{
    if(connected) //проверить, подключена ли ардуино
    {
        for(int i = 0; i<numberOfCommands; i++) //перебираем команды
        {
            int bufferSize = strlen(commands[i]); // получаем размер буфера, необходимый для этой команды
            char charArray[bufferSize]; //вспомогательный массив символов
            memcpy(charArray,commands[i],bufferSize);//копируем команду в массив символов
            charArray[bufferSize]=0; //убедитесь, что в конце есть символ остановки
            WriteSerialPort(charArray); //команда готова к отправке, отправляем.
        }
    }
}
int WriteSerialPort(const char *buffer)
{
    printf("PC: Now writing: ");
    int n_written = 0; //сколько байт было записано

    n_written = write(fd, buffer, strlen(buffer)); // пишем команду и возвращаем сколько байт было записано

    printf("\n");

    //проверка отправленных и возвращенных байтов (ошибка, ничего или x отправленных байтов)
    if(n_written<0)
    {
        printf("PC: Error %d from %s \n",errno, strerror(errno));
    }
    else if(n_written == 0)
    {
        printf("PC: Nothing was written  \n");
    }
    else
    {
        printf("PC: Written %i bytes  \n", n_written);
    }

}

int ReadSerialPort(char *buffer, unsigned int buff_size)
{
    //проверяем, подключена ли еще ардуино
    if(connected)
    {
        //читаем серийные данные
        if(read(fd,buffer,buff_size))
            return sizeof(buffer); //возвращаем сколько байт было прочитано
        else
        {
            //иначе вывести ничего не получено
            printf("PC: Arduino not Connected (ReadSerialPort) \n");
        }
    }
}

в OpenSerialPort я в основном открываю новый fd и вызываю функции, которые устанавливают параметры связи. Я использую PrepareWriteCommand, чтобы убедиться, что команды, вводимые пользователями, отмечены символом остановки, и отправить их в WriteSerialPort, где я использую write() и печатаю, сколько байтов было отправлено.

H-файл:

#include <errno.h>
#include <fcntl.h> 
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>


#define MAX_DATA_LENGTH 256

int set_interface_attribs (int fd, int speed, int parity);
void set_blocking(int fd, int should_block);
void OpenSerialPort();
void PrepareWriteCommand(int numberOfCommands, const char *commands[numberOfCommands]);
int WriteSerialPort(const char *buffer);
int ReadSerialPort(char *buffer, unsigned int buff_size);

Главное:

#include "TableFunctions.h"
#include <stdio.h>
#include <stdlib.h>    
int main(int argc, char *argv[])
    {
        char output[MAX_DATA_LENGTH]; //массив символов для хранения вывода arduino
        int received = 0; // проверяем, получил ли read() какие-либо байты
        const char* commands[]={"test"}; // тестовая команда

        OpenSerialPort(); // открываем последовательный порт

        PrepareWriteCommand((sizeof(commands)/sizeof(commands[1])),commands); //подготавливаем команду к отправке

        usleep(500000); // ждем ответа ардуино

        while(true)
        {
            ReadSerialPort(output,MAX_DATA_LENGTH);//проверяем последовательный порт на наличие ответа
            if(strlen(output)>0) //если что-то получено
            {
                printf("PC: received %i\n",received); //если да, то сколько байт
                printf("PC: %s\n",output); //и что было получено
                usleep(500000);
            }
            else if(strlen(output)<=0) //иначе ничего не выводить
            {
                printf("PC: Nothing received\n");
            }
        }
        return 0; //заканчиваем программу
    }

Я безуспешно пытался изменить настройки флагов tty. Каждый раз, когда я запускаю код C, я вижу, что Arduino перезагружается (и после открытия нового fd я даю ему некоторое время для загрузки), но связь между ними не отправляется.

Вывод:

PC: Opening port to table 
PC: Connected
testPC: Now writing: 
PC: Written 4 bytes  

PC: received 0
PC: 

Мы будем очень признательны за любую помощь в понимании того, почему код C не может взаимодействовать с Arduino!

, 👍1

Обсуждение

Вы ждете, пока загрузчик завершит выполнение, прежде чем пытаться отправить ...?, @Majenko

@Majenko да: int fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC); спать(2000000); мне сказали, что я больше никогда не назначаю полученный в ReadSerialPort(), поэтому я исправил это, и теперь я получаю: ПК: получено 8 ПК: Я отправляю только 4 байта, поэтому arduino может отправлять вдвое больше данных за указанное время, пока ПК не прочитает их. Это не имеет значения, сейчас ПК по-прежнему ничего не показывает в буфере после того, как я вызываю read(), @Jordan

Я могу ошибаться, и 8 байтов - это размер указателя (sizeof(buf)), @Jordan

Это не имеет смысла: void PrepareWriteCommand(int numberOfCommands, const char *commands[numberOfCommands]), @Majenko

@Majenko Функция получает массив команд и циклически перебирает его. В основном просто подготавливает каждую команду (убедившись, что есть символ остановки) и отправляет для записи через USB одну за другой., @Jordan

Да, но вы не можете использовать параметр для установки размера параметра. Это не имеет никакого смысла. Вы должны использовать const char **commands или const char *commands[], так как вы используете массив указателей., @Majenko

относительно: Serial.begin(19200); как насчет количества стоповых битов? что с паритетом? как насчет количества бит данных? Все это нужно установить, @user3629249

относительно: ReadSerialPort (выход, MAX_DATA_LENGTH); Я сомневаюсь, что функция: ReadSerialPort() завершает ввод байтом NUL, поэтому любые последующие вызовы, ожидающие массива символов с завершением NUL, не будут работать. Я ожидаю, что эта функция возвращает количество прочитанных символов, которые можно использовать для вставки байта NUL в массив output[], @user3629249

относительно: ReadSerialPort (выход, MAX_DATA_LENGTH); Опубликованный код не проверяет возвращаемое значение из ReadSerialPort() И фактическая функция: ReadSerialPort() не возвращает статус ошибки, когда (например) порт Не подключен, @user3629249

относительно: printf("ПК: Сейчас пишет: "); эти данные будут оставаться в выходном потоке до тех пор, пока что-то не произойдет (например, другой вызов printf(), который (правильно) имеет '\n' как часть данных ( желательно вывод последнего символа) заставляет данные передаваться на терминал, @user3629249

вызов read() сообщит коду, есть ли что-нибудь для чтения. Ожидание произвольного времени не является хорошим методом. Гораздо лучше продолжать вызывать ReadSerialPort(), пока не будут видны некоторые данные., @user3629249

OT: относительно: printf("ПК: Ошибка %d при открытии %s: %s \n", errno, portname, strerror(errno)); Сообщения об ошибках должны выводиться в stderr, а не stdout. Предложить: `fprintf( stderr, "ПК: Ошибка %d при открытии %s: %s \n", errno, имя порта, strerror(errno));, @user3629249

функции в коде, которые обрабатывают обмен данными ввода-вывода, не могут сообщить вызывающему абоненту, когда возникает ошибка, поэтому вызывающий абонент будет (в блаженстве) продолжать, как будто все хорошо, @user3629249


1 ответ


1

Проблема была в OpenSerialPort(). Я устанавливаю другую переменную fd, которая является локальной, поэтому я получаю приоритет. затем, когда OpenSerialPort() выполняется, он удаляется, а глобальный fd остается неустановленным. Вот почему я не могу ничего отправить с компьютера или на него.

Спасибо.

,