SerialSend: утиліта для роботи з віртуальним COM-портом

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).

Програма 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).

Наступна команда посилає текст 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.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 \ t SerialSend [/ 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, "\ r Trying 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-порт з командного рядка .