Серийное программирование RS485

Мне была поручена реализация протокола ModBus через двухпроводную систему RS485. (На самом деле это три провода, A / B и GND).
ModBus — не главное, но шаг до этого … простой ввод-вывод через интерфейс.

Я использую конвертер FTDI USB-RS485 для подключения хоста Linux (не взаимозаменяемого) к хосту Windows (взаимозаменяемого с другим хостом Linux, хотя я бы хотел этого избежать)

Кодировка должна быть 19200, 8, n, 1.
Но это просто не похоже на работу.

У меня нет точного кода под рукой, но в Linux я делаю это:

 int fd = open("/dev/ttyS3", O_RDWR | O_CTTY);
if(fd == -1) return "Error while opening the port";

Далее я настраиваю порт.

struct termios tty;

tcgetattr(fd, &tty);

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

tty.c_cflag  = CS8;              //Empties the cflags and sets the character width.
tty.c_cflag |= (CLOCAL | CREAD); //Sets 'recommended' options.

tty.c_lflag  = 0;
tty.c_iflag  = 0;
tty.c_oflag  = 0;

tcgetattr(fd, TCSANOW, &tty);

Контроль четности и управления потоком в настоящее время не планируются, так как конечный результат будет подключен к низкоуровневой плате, где я должен сам позаботиться о сигналах. Кроме того, нет никаких проводов, которые позволили бы «беспрепятственное общение». (В конце концов, я не хочу, чтобы символ XON / XOFF ограничивал диапазон байтов, который я могу передавать)

Все эти функции проходят правильно и данные установлены.

В Windows я открываю последовательный порт следующим образом:

DCB SP;
HANDLE hSerial = CreateFile("COM6", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if(hSerial == INVALID_HANDLE_VALUE) return "Error while opening the port";
GetCommState(hSerial, &SP);

Четность отключена, а также управление потоком. Размер байта равен 8.

Редактировать:
Так как это было задано, вот мой код для скорости передачи данных в Windows (из памяти)
SP.DCBlength = sizeof (SP);
SP.BaudRate = 19200;
SP.Parity = NOPARITY;
SP.StopBits = ONESTOPBIT;
SetCommState (hSerial, &ИП);

Опять же, все эти функции работают без нареканий.

Теперь для теста, который вызывает у меня сильную головную боль.

На хосте Linux я создаю байтовый буфер размером 256 байт.
Этот буфер заполняется символьными значениями от 0 до 255 … и затем отправляется по проводам с записью.
В то же время, другая сторона ожидает «ReadFile» для получения данных.

При такой конфигурации как для «другого хоста Linux», так и для хоста Windows поступает 256 байт … однако это НЕ числа от 0 до 255, а что-то 00 06 и т. Д.

Я могу заставить Linuxhost работать, когда я устанавливаю все члены структуры termios в 0, прежде чем устанавливать параметры, которые мне действительно нужны. Я предполагаю, что это из-за управляющих символов … однако, если я сделаю это, хост Windows получит только 4 из 256 байтов.

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

Как я реализую операцию чтения:

DWORD nBytes = 0;
char Buffer[256], *ptr = Buffer;
int Rem = 256;

while(Rem) {
ReadFile(hSerial, ptr, Rem, &nBytes, 0);
Rem -= nBytes;
ptr += nBytes;
}

//Evaluate Buffer

Чтобы отметить, я установил таймауты, но не могу вспомнить точные значения.

Изменить: так как теперь у меня есть доступ к моему рабочему месту, вот фактический (текущий) код.

const char *InitCOM(const char *TTY) {
struct termios tty;
hSerial = open(TTY, O_RDWR | O_NOCTTY | O_NDELAY);
if(hSerial == -1) return "Opening of the port failed";
fcntl(hSerial, F_SETFL, 0);
if(tcgetattr(hSerial, &tty) != 0) return "Getting the parameters failed.";
if(cfsetispeed(&tty, B19200) != 0 || cfsetospeed(&tty, B19200) != 0) return "Setting the baud rate failed.";//CFlags
//Note: I am full aware, that there's an '=', and that it makes the '&=' obsolete, but they're in there for the sake of completeness.
tty.c_cflag  = (tty.c_cflag & ~CSIZE) | CS8;    //8-bit characters
tty.c_cflag |= (CLOCAL | CREAD);und erlaubt 'Lesen'.
tty.c_cflag &= ~(PARENB | PARODD);
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;

//Input Flags
tty.c_iflag     &= ~IGNBRK;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);

//Local Flags
tty.c_lflag  = 0;

//Output Flags
tty.c_oflag  = 0;

//Control-Characters
tty.c_cc[VMIN]   = 0;
tty.c_cc[VTIME]  = 5;
if(tcsetattr(hSerial, TCSAFLUSH, &tty) != 0) return "Setting the new parameters failed";
return NULL;

}

Что касается фактического кода отправки / получения:

int main(int argc, char* argv[]) {
#if defined FOR_PC
const char *err = InitCOM("/dev/ttyUSB0");
#else
const char *err = InitCOM("/dev/ttyS3");
#endif

if(err) printf("Error while initalizing: %s ErrNum: %d\n", err, errno);
else {
/*unsigned char C[256];    //Original code with the array
int nBytes;
#ifdef FOR_PC
int Rem = 256, ReqCount = 0;
unsigned char *ptr = C;
while(Rem > 0) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(hSerial, &fds);
select(hSerial+1, &fds, NULL, NULL, NULL);
nBytes = read(hSerial, ptr, Rem);
if(nBytes > 0) {
Rem -= nBytes;
ptr += nBytes;
++ReqCount;
}
}
printf("Number of received Bytes: %d in %d sends.\n\n", 256 - Rem, ReqCount);
for(int i = 0; i < 256; ++i) {
printf("%02X ", C[i]);
if((i%32) == 31) printf("\n");
}
#else
for(int i = 0; i < 256; ++i) C[i] = i;
nBytes = write(hSerial, C, 256);
printf("\nWritten Bytes: %d\n", nBytes);
#endif*/

//Single-Byte Code
unsigned char C = 0x55;
#ifdef FOR_PC
while(true) {   //Keeps listening
fd_set fds;
FD_ZERO(&fds);
FD_SET(hSerial, &fds);
select(hSerial+1, &fds, NULL, NULL, NULL);
read(hSerial, &C, 1);
printf("Received value 0x%02X\n", C);
}
#else
write(hSerial, &C, 1);  //Sends one byte
#endif
close(hSerial);
}
return 0;

}

Что касается осциллографа: я проверил оба направления с отправкой. Оба отлично справились со своей работой.

Сигнал 0x55 — это постоянное увеличение / уменьшение на длине 50 микросекунд (как и должно быть, поэтому установка скорости передачи данных также не составляет проблем).

Так есть ли что-то в моем коде получения, что я делаю неправильно? «Выбрать» неправильно?

9

Решение

  1. Вы также устанавливаете правильную скорость передачи данных на стороне Windows?
  2. Используйте осциллограф для проверки фактических данных, присутствующих на проводе (ах). Отладка последовательной связи — это то, для чего были изобретены осциллографы. Почти. 🙂
5

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

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

На стороне отправки Linux вы должны смотреть в «сырой режим», например, cfmakeraw (). Таким образом, вас не будет беспокоить система, «помогающая» вам (например, добавление CR при отправке новой строки — действительно испортит двоичные данные …). В Microsoft есть способ сделать это, но я забыл как.

0

на стороне Windows, вы установили поле DCBLength в своей структуре DCB?

dcb.DCBlength = sizeof(dcb);
0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector