/** * Serial Port Loopback Test Program * * Function: Send and receive concurrently, exit immediately on successful receive * * Compile: gcc -std=gnu99 -o hs_serial hs_serial.c -lpthread * Run: ./hs_serial [/dev/ttyUSB0] [baudrate] */ #define _POSIX_C_SOURCE 200809L #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BUF_SIZE 4096 #define DEFAULT_BAUD 115200 #define DEFAULT_PORT "/dev/ttyUSB0" typedef struct { int fd; char port[64]; int baud_rate; int data_bits; int stop_bits; char parity; volatile int received; // flag: data received volatile int running; // flag: keep running } serial_port_t; static serial_port_t g_serial; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void signal_handler(int sig) { (void)sig; g_serial.running = 0; } void setup_signal_handler(void) { struct sigaction sa; sa.sa_handler = signal_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); } speed_t get_baud_rate(int baud) { switch(baud) { case 50: return B50; case 75: return B75; case 110: return B110; case 134: return B134; case 150: return B150; case 200: return B200; case 300: return B300; case 600: return B600; case 1200: return B1200; case 1800: return B1800; case 2400: return B2400; case 4800: return B4800; case 9600: return B9600; case 19200: return B19200; case 38400: return B38400; case 57600: return B57600; case 115200: return B115200; case 230400: return B230400; case 460800: return B460800; case 500000: return B500000; case 576000: return B576000; case 921600: return B921600; case 1000000: return B1000000; case 1152000: return B1152000; case 1500000: return B1500000; case 2000000: return B2000000; case 2500000: return B2500000; case 3000000: return B3000000; case 3500000: return B3500000; case 4000000: return B4000000; default: return B115200; } } int serial_open(serial_port_t *serial) { struct termios options; serial->fd = open(serial->port, O_RDWR | O_NOCTTY | O_NDELAY); if (serial->fd == -1) { perror("Failed to open serial port"); return -1; } fcntl(serial->fd, F_SETFL, 0); if (tcgetattr(serial->fd, &options) != 0) { perror("Failed to get serial config"); close(serial->fd); return -1; } cfsetispeed(&options, get_baud_rate(serial->baud_rate)); cfsetospeed(&options, get_baud_rate(serial->baud_rate)); options.c_cflag |= CLOCAL | CREAD; options.c_cflag &= ~CSIZE; switch(serial->data_bits) { case 5: options.c_cflag |= CS5; break; case 6: options.c_cflag |= CS6; break; case 7: options.c_cflag |= CS7; break; default: options.c_cflag |= CS8; break; } if (serial->stop_bits == 2) options.c_cflag |= CSTOPB; else options.c_cflag &= ~CSTOPB; switch(serial->parity) { case 'N': options.c_cflag &= ~PARENB; options.c_iflag &= ~INPCK; break; case 'E': options.c_cflag |= PARENB; options.c_cflag &= ~PARODD; options.c_iflag |= INPCK; break; case 'O': options.c_cflag |= PARENB; options.c_cflag |= PARODD; options.c_iflag |= INPCK; break; default: options.c_cflag &= ~PARENB; options.c_iflag &= ~INPCK; break; } options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); options.c_oflag &= ~OPOST; options.c_cc[VMIN] = 0; options.c_cc[VTIME] = 10; tcflush(serial->fd, TCIOFLUSH); if (tcsetattr(serial->fd, TCSANOW, &options) != 0) { perror("Failed to set serial config"); close(serial->fd); return -1; } return 0; } void serial_close(serial_port_t *serial) { if (serial->fd > 0) { close(serial->fd); serial->fd = -1; } } int serial_write(serial_port_t *serial, const char *data, int length) { int bytes_written = 0; int ret; if (serial->fd < 0) return -1; while (bytes_written < length) { ret = write(serial->fd, data + bytes_written, length - bytes_written); if (ret < 0) { if (errno == EAGAIN) { usleep(1000); continue; } return -1; } bytes_written += ret; } return bytes_written; } // ========== Send Thread ========== void *send_thread(void *arg) { serial_port_t *serial = (serial_port_t *)arg; char buffer[BUF_SIZE]; time_t now; time_t start_time = time(NULL); // 记录开始时间 printf("[Send Thread] Started\n"); while (serial->running) { // 判断是否超过5秒 if (time(NULL) - start_time >= 2) { printf("[Send] 5 seconds reached, exit loop\n"); break; } pthread_mutex_lock(&mutex); if (!serial->running) { pthread_mutex_unlock(&mutex); break; } now = time(NULL); int len = snprintf(buffer, BUF_SIZE, "%ld Serial Loopback Test\n", (long)now); printf("[Send] Sending %d bytes...\n", len); int ret = serial_write(serial, buffer, len); if (ret > 0) { printf("[Send] Sent %d bytes successfully\n", ret); } else { printf("[Send] Send failed\n"); } pthread_mutex_unlock(&mutex); usleep(500000); // 500ms } printf("[Send Thread] Exited\n"); return NULL; } // ========== Receive Thread ========== void *receive_thread(void *arg) { serial_port_t *serial = (serial_port_t *)arg; char buffer[BUF_SIZE]; time_t start_time = time(NULL); // 开始时间 printf("[Receive Thread] Started\n"); while (serial->running) { // 5秒超时判断 if (time(NULL) - start_time >= 2) { printf("[Receive] FAILED: No data received within 5 seconds\n"); serial->running = 0; break; } fd_set read_fds; struct timeval timeout; FD_ZERO(&read_fds); FD_SET(serial->fd, &read_fds); timeout.tv_sec = 1; timeout.tv_usec = 0; int ret = select(serial->fd + 1, &read_fds, NULL, NULL, &timeout); if (ret > 0 && FD_ISSET(serial->fd, &read_fds)) { ret = read(serial->fd, buffer, BUF_SIZE - 1); if (ret > 0) { buffer[ret] = '\0'; printf("[Receive] Received %d bytes: %s\n", ret, buffer); printf("SUCCESSFUL PASSED - Data received\n"); serial->running = 0; break; } } } printf("[Receive Thread] Exited\n"); return NULL; } // ========== Main ========== int main(int argc, char *argv[]) { pthread_t send_tid, recv_tid; printf("========== Serial Loopback Test ==========\n\n"); // Default settings strncpy(g_serial.port, DEFAULT_PORT, sizeof(g_serial.port) - 1); g_serial.baud_rate = DEFAULT_BAUD; g_serial.data_bits = 8; g_serial.stop_bits = 1; g_serial.parity = 'N'; g_serial.fd = -1; g_serial.received = 0; g_serial.running = 1; // Parse arguments if (argc > 1) strncpy(g_serial.port, argv[1], sizeof(g_serial.port) - 1); if (argc > 2) g_serial.baud_rate = atoi(argv[2]); printf("Serial Port: %s\n", g_serial.port); printf("Baud Rate: %d\n", g_serial.baud_rate); printf("===========================================\n\n"); setup_signal_handler(); // Open serial port if (serial_open(&g_serial) != 0) { fprintf(stderr, "Cannot open serial port %s\n", g_serial.port); return EXIT_FAILURE; } printf("Serial port opened successfully!\n"); printf("Note: Please ensure TX/RX pins are connected for loopback test\n\n"); // Create send thread if (pthread_create(&send_tid, NULL, send_thread, &g_serial) != 0) { perror("Failed to create send thread"); serial_close(&g_serial); return EXIT_FAILURE; } // Create receive thread if (pthread_create(&recv_tid, NULL, receive_thread, &g_serial) != 0) { perror("Failed to create receive thread"); g_serial.running = 0; pthread_join(send_tid, NULL); serial_close(&g_serial); return EXIT_FAILURE; } // Wait for threads to finish pthread_join(send_tid, NULL); pthread_join(recv_tid, NULL); // Close serial port serial_close(&g_serial); printf("========== Test Finished ==========\n"); return EXIT_SUCCESS; }