сигналы — C / C ++ читает порт UART и отображает результаты

Я написал C-программу, которая должна читать порт RxD UART и отображать результаты, как только появится какая-либо информация. Для достижения этого я использую сигнал-обработчик сигнала SIGIO

Прочитайте код программы c

#include <iostream>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <poll.h>
#include <time.h>
#include <string.h>
#define BAUDRATE B19200
#define PORT "/dev/ttyO4"#define _POSIX_SOURCE 1

int fd;
void signal_handler_IO(int status);
void set_port_settings();
char buff[255];
sig_atomic_t flag=0;
using namespace std;

int main ()
{
set_port_settings();

for(;;){

if(flag !=0)
{
//printf( "sync : 0x%X\n", buff[1]);
//printf ( "PID: 0x%X\n", buff[2]);
printf ( "D0: 0x%X\n",  buff[4]);
printf ( "D1: 0x%X\n",  buff[5]);
printf ( "D2: 0x%X\n",  buff[6]);
printf ( "D3: 0x%X\n",  buff[7]);
printf ( "D4: 0x%X\n",  buff[8]);
printf ( "D5: 0x%X\n",  buff[9]);
printf ( "D6: 0x%X\n",  buff[10]);
printf ( "D7: 0x%X\n",  buff[11]);
printf ( "CHK: 0x%X\n", buff[12]);
flag = 0;
}

}
}void signal_handler_IO(int status)
{
if(flag !=1)
{
read(fd, &buff, sizeof(buff));
flag = 1;
}
}

void set_port_settings()
{
struct termios oldtio, newtio;
struct sigaction saio;
fd = open(PORT, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd<0) {perror(PORT); exit(-1);}

saio.sa_handler=signal_handler_IO;
sigemptyset(&saio.sa_mask);
saio.sa_flags=SA_RESTART;
sigaction(SIGIO, &saio,NULL);

fcntl (fd, F_SETOWN, getgid());
fcntl(fd, F_SETFL, FASYNC);

tcgetattr(fd, &oldtio); perror("tsgetattr");newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD ; perror("c_cflag");
newtio.c_iflag = IGNPAR | IXON ; perror("c_iflag");
newtio.c_oflag = 0; perror("c_oflag");
newtio.c_lflag = ICANON | ISIG ; perror("c_lflag");
newtio.c_cc[VMIN]=8;perror("c_cc[VMIN]");
newtio.c_cc[VTIME]=1; perror("c_cc[VTIME]");
newtio.c_cc[VSTART]=0x55;

tcflush(fd, TCIFLUSH); perror("TCFLUSH");

tcsetattr(fd, TCSANOW, &newtio); perror("tcsetattr");
}

Проблема, с которой я столкнулся, заключается в том, что когда программа считывает данные и начинает распечатывать результаты, распечатанная информация как-то верна, просто напечатана (или прочитана) в неправильном месте.

Я пишу в порт, используя другую C-программу. Я пытался сделать это из той же программы C, но безуспешно, чтобы написать и прочитать из той же программы C. Таким образом, я держу открытыми две оболочки: в одной из них я запускаю программу записи, а в другой — программу чтения, чтобы отобразить то, что она смогла прочитать.

Написать код программы C

#include <iostream>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#define BAUDRATE9600 B19200
#define PORT "/dev/ttyO4"#define _POSIX_SOURCE 1

using namespace std;

int main() {
int fd;
char buffer[255];
struct termios oldtio, newtio;
struct sigaction saio;
fd = open(PORT, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd<0) {perror(PORT); exit(-1);}

tcgetattr(fd, &oldtio);

newtio.c_cflag = BAUDRATE9600 | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
newtio.c_lflag = 0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &newtio);

char SYNC  [] = {0x55};
char PID [] = {0x97};
char data0 [] = {0x25};
char data1 [] = {0xFF};
char data2 [] = {0x00};
char data3 [] = {0x64};
char data4 [] = {0x01};
char data5 [] = {0xFF};
char data6 [] = {0xFF};
char data7 [] = {0xFC};
char checksum [] ={0xE0};

за (;;) {

ioctl(fd, TIOCSBRK);
usleep(676); // 13 bits, 1 bit = 52us

ioctl(fd,TIOCCBRK);
usleep(260); // 5 bits

write(fd, SYNC, sizeof(SYNC));
write(fd, PID, sizeof(PID));
write(fd, data0, sizeof(data0));
write(fd, data1, sizeof(data1));
write(fd, data2, sizeof(data2));
write(fd, data3, sizeof(data3));
write(fd, data4, sizeof(data4));
write(fd, data5, sizeof(data5));
write(fd, data6, sizeof(data6));
write(fd, data7, sizeof(data7));
write(fd, checksum, sizeof(checksum));

usleep(10000);
close (fd); }

Когда я запускаю обе программы, чтобы проверить, ЧИТАТЬ Программы работают как надо, я вижу, что данные считываются, но не совсем так, как они записаны в порт.

пример данных, прочитанных в

d0: 0x7C
d1: 0x66
d2: 0x1
d3: 0xE0
d4: 0x4C
d5: 0x7C
d6: 0x8
d7: 0x60
CHK: 0x60

d0: 0x1
d1: 0xE0
d2: 0x4C
d3: 0x7C
d4: 0x8
d5: 0x60
d6: 0xFC
d7: 0x60
CHK: 060

Я надеюсь, что кто-нибудь сможет указать, где я допустил ошибку и что я должен сделать, чтобы иметь возможность считывать данные с порта UART без проблем.

0

Решение

Проблема, с которой я столкнулся, заключается в том, что когда программа считывает данные и начинает распечатывать результаты, распечатанная информация как-то верна, просто напечатана (или прочитана) в неправильном месте.

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

Программы чтения и записи неправильно инициализируют последовательный порт в неполное (и, следовательно, неизвестное) состояние. Как прокомментировал @Swanand, программа записи преждевременно закрывает свой файловый дескриптор.

Программа чтения без необходимости использует асинхронные события ввода / вывода для выполнения читать() системные вызовы и не проверка кода возврата. Затем программа чтения безоговорочно распечатывает буфер независимо от того, есть ли фактические данные чтения.

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



Некоторые из конкретных ошибок в вашем коде:


Отсутствие правильного и последовательного форматирования.


void set_port_settings()
{
struct termios oldtio, newtio;

newtio является автоматической переменной и поэтому не инициализируется.
Программа выборочно присваивает значения нескольким элементам структуры и оставляет другие неопределенными.
Эти неопределенные элементы в структуре termios передаются через tcsetattr () Системный вызов может привести к непредсказуемой и / или ненадежной работе последовательного порта.

Ты должен учиться Правильная настройка режимов работы терминала а также Руководство по последовательному программированию для операционных систем POSIX.


newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD ; perror("c_cflag");
newtio.c_iflag = IGNPAR | IXON ; perror("c_iflag");

Это неправильный метод установки атрибутов termios.
Обратитесь к упомянутым руководствам для правильной техники.
Вы утверждали, что исправили эти ошибки в этот вопрос, все же здесь они все еще.

Безусловный PError () после каждого назначения неправильно, так как это не системный вызов.

Включение IXON кажется неуместным, так как это программа чтения, которая не делает никакого вывода. (IXON позволяет мягкое управление потоком для вывода.)


newtio.c_lflag = ICANON | ISIG ; perror("c_lflag");

Включение ICANON кажется неверным, так как данные из программы записи — это не форматированный текст, а двоичные данные. Эта программа должна настраивать сырой режим вместо канонического режима.

Включение ISIG кажется неверным, так как значения запускающих символов в VINTR, VQUIT, VSUSP, или же VDSUSP все не определены в этой неинициализированной структуре termios.
Поскольку программа записи создает условия прерывания, сигнал от прерывания может быть сгенерирован, но имеет нежелательные побочные эффекты, то есть очищает очереди.

Безусловный PError () после каждого назначения неправильно, так как это не системный вызов.


tcsetattr(fd, TCSANOW, &newtio); perror("tcsetattr");

Код возврата этого (и всех других) системных вызовов необходимо проверить.
Безусловный PError () вызов неверен.


Я написал C-программу, которая должна читать порт RxD UART и отображать результаты, как только появится какая-либо информация. Для этого я использую сигнал SIGIO signal_handler

Ваша программа не имеет прямого доступа к порту RXD UART.
Это драйвер последовательного порта, который считывает фактические данные из регистра UART RxD.
Поскольку вы настроили последовательный порт для канонического ввода, данные сохраняются в буфере дисциплины строк, а затем копируются в системный буфер.
читать() Системный вызов в вашей программе извлекает данные из системного буфера.
SIGIO не ускоряет чтение вашей программой.

void signal_handler_IO(int status)
{
if(flag !=1)
{
read(fd, &buff, sizeof(buff));
flag = 1;
}
}

Этот обработчик сигналов не нужен, если ваша программа выполнила обычную блокировку читать(). Ваша предпосылка для использования неблокирующего ввода-вывода и SIGIO, похоже, неверна. Программа не использует асинхронный ввод-вывод эффективно, так что вы могли бы также упростить программу.

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

Поскольку программа чтения не позволяет ни IGNBRK или же BRKINT, условие прерывания принимается вашей программой в виде нулевого байта 0x00 вместе с фактическими данными сообщения.


Программа чтения должна искать и поддерживать выравнивание сообщений, используя байты «sync» и «checkum». Ваша программа в настоящее время не имеет никакого метода для проверки или достижения выравнивания сообщений.
Основной алгоритм для выполнения этого в этот ответ

3

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

Если вы читаете / записываете не ascii данные в файлы, вы должны открыть их с помощью ‘O_BINARY’.

Как следующий намек:
Используйте консоль для чтения или записи данных, чтобы увидеть, какая программа неисправна. Просто используйте «эхо» для этого.

Чтобы увидеть, что ваша программа читает или пишет изнутри, используйте ‘strace -xfo dump your_prog’ и посмотрите в файл ‘dump’, чтобы увидеть, какие символы были отправлены / прочитаны с вашего uart.

Следующие инструкции кажутся мне бессмысленными! У вас не будет возможности уснуть точно с любой скоростью, пока у вас не будет жесткого ядра и оборудования реального времени. На x86 у вас будут отклонения более 100 мс! для сна, если запущен другой процесс. Например, каждый диск убьет ваше время.

ioctl(fd, TIOCSBRK);
usleep(676); // 13 bits, 1 bit = 52us

ioctl(fd,TIOCCBRK);
usleep(260); // 5 bits

Для отправки перерыва используйте

  tcsendbreak()
1

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