Программирование PC SerialSend: утилита для работы с виртуальным COM-портом Thu, November 21 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


SerialSend: утилита для работы с виртуальным COM-портом Печать
Добавил(а) microsin   

SerialSend это маленькое приложение Windows, запускаемое из командной строки, которая создана автором (Ted Burke [1]) для отправки строк текста через последовательный порт. Программа удобна для использования вместе с устройствами на микроконтроллерах, которые подключаются к компьютеру через порт USB как виртуальный последовательный COM-порт (VCP).

Примечание: это могут быть различные устройства, например подключенный к USB микроконтроллер AVR, в который записан проект VCP на основе библиотеки V-USB (см. [2]), либо микроконтроллер AVR с аппаратным USB (проект на основе библиотеки LUFA [3]). В этих выше перечисленных случаях микроконтроллеры AVR подключаются к USB напрямую, без специальных микросхем. Также микроконтроллеры AVR могут подключаться к USB через специальные переходники USB-RS232TTL на чипах FT232RL, CP1021, CH340G, ATmega16U2 и т. п. (так называемые схемы USB-to-serial, по такому принципу подключаются к UART через USB популярные платы Arduino).

USB 2 0 To Ttl Uart 5pin CP2102 Module Serial Converter USB 2 0 To Ttl Uart 6pin CP2102 Module Serial Converter USB2.0-TTL-UART-6PIN-CP2102 usb-to-uart-cp2102-rs232-ttl-6pin

Программа SerialSend позволит Вам:

• Послать строку произвольных символов текста в устройство через последовательный порт с помощью одной команды.
• Послать текст из простых консольных приложений в аппаратные устройства через последовательный порт с использованием функции "system".
• Указать скорость передачи (baud rate) последовательного канала связи.
• Указать номер последовательного порта, куда будет послан текст.
• Есть возможность автоматически найти доступный порт с самым большим номером (полезно для конвертеров USB VCP, или USB-to-serial, потому что именно они чаще всего соответствуют самым большим номера COM-портов в операционной системе Windows).

Причина, по которой была добавлена последняя возможность, следующая. Часто Windows присваивает различные номера COM-портов даже для одного и того же устройства, если он подключается в разные порты USB (прим. переводчика: это происходит в том случае, если в устройстве USB VCP нет встроенного серийного номера; тогда Windows сама генерирует идентификаторы для такого устройства VCP, привязываясь к номеру порта USB). Чаще всего при подключении переходника USB VCP к порту USB операционная система присваивает ему следующий по порядку не используемый номер COM-порта, так что номер COM-порта получается самый большой из всех доступных COM-портов. Поэтому автоматическое подключение к COM-порту по самому большому номеру в таком случае оказывается полезным, потому что не надо специально задавать номер COM-порта для подключения.

Автор предоставил полный исходный код на языке C для своей утилиты SerialSend (см. врезку). Код компилировался в среде MinGW (gcc), как это показано в комментариях к коду, но также можно скомпилировать SerialSend под управлением Visual C++ или другого компилятора Windows C/C++. Также Вы можете загрузить и использовать уже скомпилированный вариант (SerialSend.exe, 54 KB, date: 8-4-2015).

[Примеры использования]

Примечание: если передаваемый текст содержит пробелы, то он должен быть заключен в двойные кавычки.

Следующая команда посылает символы abc 123 через доступный последовательный порт с самым большим номером и скоростью по умолчанию (38400 baud).

SerialSend.exe "abc 123"

Следующая команда посылает текст Hello world! через самый большой по номеру и доступный последовательный порт на скорости 9600 baud.

SerialSend.exe /baudrate 9600 "Hello world!"

Следующая команда посылает текст S120 E360 через COM10 со скоростью по умолчанию (38400 baud). Если порт COM10 не доступен в системе, то вместо него будет использован доступный порт с самым большим номером.

SerialSend.exe /devnum 10 "S120 E360"

Произвольные байты, включая не печатаемые символы (такие, как табуляция TAB, символы возврата каретки CR и перевода строки LF), могут быть добавлены в строку как шестнадцатеричные числа с помощью опции /hex командной строки и esc-символа \x в задаваемом тексте. Например, следующая команда отправляет строку abc, за которой следует символ перевода строки LF (line feed, шестнадцатеричное значение 0x0A). В этом примере получается, что всего будет отправлено 4 байта.

SerialSend.exe /hex "abc\x0A"

Также вместе с опцией /hex можно использовать esc-последовательности \n и \r для вставки в строку символа перевода строки (LF) и возврата каретки (CR) соответственно. Например, следующая команда отправит строку Hello, за которой идут символы CR и LF (всего будет отправлено 7 байт).

SerialSend.exe /hex "Hello\r\n"

Опция командной строки /closedelay позволяет вставить задержку (указанную в миллисекундах) после того, как текст будет отправлен, но до того, как последовательный порт будет закрыт. Это может понадобиться, когда нужно посылать порции данных в устройство, и необходимо при этом дать ему время на обработку и ответ для каждой порции. Например, следующая команда посылает символы ABCD и CR в порт COM5, и после этого делает задержку 500 мс, и после истечения этой задержки COM5 будет закрыт (освобожден).

SerialSend.exe /devnum 5 /closedelay 500 "ABCD\r"

Пример скриншота запуска SerialSend в консоли:

SerialSend screenshot

// [SerialSend.c]
// Эта программа посылает текст через последовательный порт
// Автор Ted Burke, последнее обновление 8-4-2015
//
// Текст для отправки указывается в аргументах командной строки.
// По умолчанию используется COM-порт с самым большим найденным номером.
// Используемая по умолчанию скорость 38400 baud.
//
// Как компилировать с помощью установленной системы MinGW:
//
//      gcc -o SerialSend.exe SerialSend.c
//
// Как компилировать компилятором cl компании Microsoft:
//
//      cl SerialSend.c
//
// Пример запуска (отправка последовательности символов S365 E120):
//
//      SerialSend.exe "S356 E120"
//
#include < windows.h >
#include < stdio.h >
    
int main(int argc, char *argv[])
{
    // Декларация переменных и структур:
    int m, n;
    unsigned char buffer[MAX_PATH];
    unsigned char text_to_send[MAX_PATH];
    unsigned char digits[MAX_PATH];
    int baudrate = 38400;
    int dev_num = 50;
    int parse_hex_bytes = 0;
    int close_delay = 0;
    char dev_name[MAX_PATH];
    HANDLE hSerial;
    DCB dcbSerialParams = {0};
    COMMTIMEOUTS timeouts = {0};
     
    // Вывод приветственного сообщения:
    fprintf(stderr, "SerialSend (last updated 8-4-2015)\n");
    fprintf(stderr, "See http://batchloaf.com for more information\n");
     
    // Парсинг аргументов командной строки:
    int argn = 1;
    strcpy(buffer, "");
    while(argn < argc)
    {
        if (strcmp(argv[argn], "/baudrate") == 0)
        {
            // Parse baud rate
            if (++argn < argc && ((baudrate = atoi(argv[argn])) > 0))
            {
                fprintf(stderr, "%d baud specified\n", baudrate);
            }
            else
            {
                fprintf(stderr, "Baud rate error\n");
                return 1;
            }
        }
        else if (strcmp(argv[argn], "/devnum") == 0)
        {
            // Обработка номера устройства. SerialSend начинает поиск доступных
            // COM-портов с этого номера, постепенно уменьшая его до нуля.
            if (++argn < argc)
            {
                dev_num = atoi(argv[argn]);
                fprintf(stderr, "Device number %d specified\n", dev_num);
            }
            else
            {
                fprintf(stderr, "Device number error\n");
                return 1;
            }
        }      
        else if (strcmp(argv[argn], "/closedelay") == 0)
        {
            // Обработка задержки перед закрытием порта. После передачи
            // указанного текста SerialSend выполнит задержку на это
            // количество миллисекунд, после чего закроет COM-порт.
            if (++argn < argc)
            {
                close_delay = atoi(argv[argn]);
                fprintf(stderr, "Delay of %d ms specified before closing COM port\n", close_delay);
            }
            else
            {
                fprintf(stderr, "Close delay error\n");
                return 1;
            }
        }      
        else if (strcmp(argv[argn], "/hex") == 0)
        {
            // Обработка hex-флага для передаваемого байта.
            // Если этот флаг установлен, то в строку передачи можно
            // добавить произвольный байт, указывая его через нотацию '\x'.
            // Например, команда "SerialSend /hex Hello\x0D"
            // отправит 6 байт, где последним будет байт перевода строки CR
            // (символ '\r', у которого код 0x0D).
            parse_hex_bytes = 1;
        }
        else
        {
            // Этот аргумент командной строки будет отправляемым текстом:
            strcpy(buffer, argv[argn]);
        }
           
        // Переход к следующему аргументу командной строки:
        argn++;
    }
       
    // Проверка, предоставлен ли для передачи какой-нибудь текст:
    if (strlen(buffer) == 0)
    {
        fprintf(stderr, "Usage:\n\n\tSerialSend [/baudrate BAUDRATE] ");
        fprintf(stderr, "[/devnum DEVICE_NUMBER] [/hex] \"TEXT_TO_SEND\"\n");
        return 1;
    }
       
    // Если hex-парсинг разрешен, то текст для передачи модифицируется:
    n = 0; m = 0;
    while(n < strlen(buffer))
    {
        if (parse_hex_bytes && buffer[n] == '\\')
        {
            n++;
            if (buffer[n] == '\\') text_to_send[m] = '\\';
            else if (buffer[n] == 'n') text_to_send[m] = '\n';
            else if (buffer[n] == 'r') text_to_send[m] = '\r';
            else if (buffer[n] == 'x')
            {
                digits[0] = buffer[++n];
                digits[1] = buffer[++n];
                digits[2] = '\0';
                text_to_send[m] = strtol(digits, NULL, 16);
            }
        }
        else
        {
            text_to_send[m] = buffer[n];
        }
           
        m++; n++;
    }
    text_to_send[m] = '\0'; // Null-символ, завершающий строку.
       
    // Открыть доступный порт с самым большим номером:
    fprintf(stderr, "Searching serial ports...\n");
    while(dev_num >= 0)
    {
        fprintf(stderr, "\r                        ");
        fprintf(stderr, "\rTrying COM%d...", dev_num);
        sprintf(dev_name, "\\\\.\\COM%d", dev_num);
        hSerial = CreateFile(
        dev_name, GENERIC_READ|GENERIC_WRITE, 0, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
        if (hSerial == INVALID_HANDLE_VALUE) dev_num--;
        else break;
    }
   
    if (dev_num < 0)
    {
        fprintf(stderr, "No serial port available\n");
        return 1;
    }
   
    fprintf(stderr, "OK\n");
   
    // Установка параметров устройства (38400 baud, 1 start bit,
    // 1 stop bit, no parity)
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    if (GetCommState(hSerial, &dcbSerialParams) == 0)
    {
        fprintf(stderr, "Error getting device state\n");
        CloseHandle(hSerial);
        return 1;
    }
    //dcbSerialParams.BaudRate = CBR_38400;
    dcbSerialParams.BaudRate = baudrate;
    dcbSerialParams.ByteSize = 8;
    dcbSerialParams.StopBits = ONESTOPBIT;
    dcbSerialParams.Parity = NOPARITY;
    if(SetCommState(hSerial, &dcbSerialParams) == 0)
    {
        fprintf(stderr, "Error setting device parameters\n");
        CloseHandle(hSerial);
        return 1;
    }
   
    // Настройки таймаута COM-порта:
    timeouts.ReadIntervalTimeout = 50;
    timeouts.ReadTotalTimeoutConstant = 50;
    timeouts.ReadTotalTimeoutMultiplier = 10;
    timeouts.WriteTotalTimeoutConstant = 50;
    timeouts.WriteTotalTimeoutMultiplier = 10;
    if(SetCommTimeouts(hSerial, &timeouts) == 0)
    {
        fprintf(stderr, "Error setting timeouts\n");
        CloseHandle(hSerial);
        return 1;
    }
       
    // Отправка указанного текста:
    DWORD bytes_written, total_bytes_written = 0;
    fprintf(stderr, "Sending text... ");
    while(total_bytes_written < m)
    {
        if(!WriteFile(hSerial, text_to_send + total_bytes_written,
            m - total_bytes_written, &bytes_written, NULL))
        {
            fprintf(stderr, "Error writing text to %s\n", dev_name);
            CloseHandle(hSerial);
            return 1;
        }
           
        total_bytes_written += bytes_written;
    }
    fprintf(stderr, "\n%d bytes written to %s\n", total_bytes_written, dev_name);
     
    // Сброс (Flush) буфера передачи перед закрытием последовательного порта:
    FlushFileBuffers(hSerial);
    if (close_delay > 0)
    {
        fprintf(stderr, "Delaying for %d ms before closing COM port... ", close_delay);
        Sleep(close_delay);
        fprintf(stderr, "OK\n");
    }
     
    // Закрытие последовательного порта:
    fprintf(stderr, "Closing serial port...");
    if (CloseHandle(hSerial) == 0)
    {
        fprintf(stderr, "Error\n", dev_name);
        return 1;
    }
    fprintf(stderr, "OK\n");
   
    // Нормальный выход:
    return 0;
}

[Ссылки]

1. SerialSend site:batchloaf.wordpress.com.
2. USB консоль для управления радиолюбительскими приборами.
3. LUFA - бесплатная библиотека USB для микроконтроллеров Atmel AVR.
4. Как посылать символы в COM-порт из командной строки.

 

Комментарии  

 
+1 #1 Михаил 22.04.2020 09:05
Спасибо, программка помогла!
Цитировать
 

Добавить комментарий


Защитный код
Обновить

Top of Page