SAMA5D3开发板USB设备读写:数据读取方法与写入验证问询
Hey there! Let's work through your two questions about reading from and verifying writes to your USB-connected device on the SAMA5D3 board. First, let's break down the issues in your current code and fix them step by step.
Your current reading logic has a couple of key issues that are preventing you from getting data properly:
- Uninitialized/incorrect termios configuration: The
speedvariable isn't assigned a value, and settingc_iflag/c_oflagtospeeddoesn't make sense. For custom baud rates withBOTHER, you need to setc_ispeedandc_ospeeddirectly. - Timing issue with FIONREAD: You're checking for available bytes immediately after writing, but the device might not have sent a response yet—especially in non-blocking mode.
- Non-blocking mode pitfalls: Using
O_NONBLOCKmeansreadwill return immediately if no data is ready, which is why you're hitting the "error" case often.
Here's how to fix this:
Step 1: Fix the Termios Configuration
First, properly set up your serial port parameters (adjust baud rate to match your device):
struct termios usb_parm; // Initialize with current port settings tcgetattr(fd1, &usb_parm); // Reset flags and set custom baud rate with BOTHER usb_parm.c_cflag &= ~CBAUD; usb_parm.c_cflag |= BOTHER; usb_parm.c_ispeed = 9600; // Set your desired baud rate here usb_parm.c_ospeed = 9600; // Configure for raw mode (tweak based on your device's requirements) usb_parm.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); usb_parm.c_oflag &= ~OPOST; usb_parm.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); usb_parm.c_cflag &= ~(CSIZE | PARENB); usb_parm.c_cflag |= CS8; // 8 data bits // Apply settings immediately retval = tcsetattr(fd1, TCSANOW, &usb_parm); if (retval != 0) { perror("Failed to set serial attributes"); close(fd1); return -1; }
Step 2: Improve the Reading Logic
Instead of checking FIONREAD once, use select() to wait for data to arrive (this handles timing properly, whether you use blocking or non-blocking mode):
char read_buff[64] = {0}; // Fixed-size buffer with room for null terminator int bytes_read = 0; fd_set readfds; struct timeval tv; // Wait up to 2 seconds for a response tv.tv_sec = 2; tv.tv_usec = 0; FD_ZERO(&readfds); FD_SET(fd1, &readfds); int select_ret = select(fd1 + 1, &readfds, NULL, NULL, &tv); if (select_ret > 0 && FD_ISSET(fd1, &readfds)) { int byteavailable = 0; ioctl(fd1, FIONREAD, &byteavailable); if (byteavailable > 0) { bytes_read = read(fd1, read_buff, sizeof(read_buff) - 1); // Leave space for null terminator if (bytes_read > 0) { read_buff[bytes_read] = '\0'; // Ensure valid string printf("Read buff=%s\n", read_buff); printf("Bytes read=%d\n", bytes_read); } else { perror("Read failed"); } } } else if (select_ret == 0) { printf("Timeout: No data received from device\n"); } else { perror("Select error"); }
To verify your write was successful, you need to check two key things: the return value of write(), and optionally wait for device acknowledgment.
Step 1: Validate the Write Return Value
The write() function returns the number of bytes actually written. Compare this to the number of bytes you intended to send (note: don't send the null terminator unless your device expects it—use strlen() instead of sizeof()):
char write_buff[] = "q,e"; int expected_bytes = strlen(write_buff); int bytes_written = write(fd1, write_buff, expected_bytes); if (bytes_written == expected_bytes) { printf("Successfully wrote all %d bytes: %s\n", bytes_written, write_buff); } else if (bytes_written == -1) { perror("Write failed entirely"); } else { printf("Only wrote %d of %d bytes\n", bytes_written, expected_bytes); }
Step 2: Wait for Data to Be Transmitted
Use tcdrain() to ensure all data in the output buffer is sent to the device before proceeding:
tcdrain(fd1); // Blocks until all output is transmitted
Step 3: (Optional) Read a Device Acknowledgment
Many devices send an ACK message after receiving valid data. If your device does this, use the reading logic from question 1 to listen for this ACK as confirmation the data was received and processed.
Here's the complete, fixed version of your main function:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <termios.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #include <sys/select.h> int main() { int fd1, retval; struct termios usb_parm; // Open port with O_RDWR (simpler than separate read/write flags) fd1 = open("/dev/ttyACM0", O_RDWR | O_NOCTTY); if (fd1 > 0) { printf("Device detected\n"); } else { perror("Failed to open port"); return -1; } // Configure serial port parameters tcgetattr(fd1, &usb_parm); // Set custom baud rate usb_parm.c_cflag &= ~CBAUD; usb_parm.c_cflag |= BOTHER; usb_parm.c_ispeed = 9600; usb_parm.c_ospeed = 9600; // Set raw mode usb_parm.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); usb_parm.c_oflag &= ~OPOST; usb_parm.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); usb_parm.c_cflag &= ~(CSIZE | PARENB); usb_parm.c_cflag |= CS8; // Apply settings retval = tcsetattr(fd1, TCSANOW, &usb_parm); if (retval != 0) { perror("Failed to set serial attributes"); close(fd1); return -1; } // Flush pending data tcflush(fd1, TCIOFLUSH); // Write data to device char write_buff[] = "q,e"; int expected_bytes = strlen(write_buff); int bytes_written = write(fd1, write_buff, expected_bytes); if (bytes_written == expected_bytes) { printf("Successfully wrote all %d bytes: %s\n", bytes_written, write_buff); tcdrain(fd1); // Wait for transmission to finish } else { perror("Write failed"); close(fd1); return -1; } // Read response char read_buff[64] = {0}; int bytes_read = 0; fd_set readfds; struct timeval tv; tv.tv_sec = 2; tv.tv_usec = 0; FD_ZERO(&readfds); FD_SET(fd1, &readfds); int select_ret = select(fd1 + 1, &readfds, NULL, NULL, &tv); if (select_ret > 0 && FD_ISSET(fd1, &readfds)) { int byteavailable = 0; ioctl(fd1, FIONREAD, &byteavailable); if (byteavailable > 0) { bytes_read = read(fd1, read_buff, sizeof(read_buff) - 1); if (bytes_read > 0) { read_buff[bytes_read] = '\0'; printf("Read buff=%s\n", read_buff); printf("Bytes read=%d\n", bytes_read); } else { perror("Read failed"); } } } else if (select_ret == 0) { printf("Timeout: No response from device\n"); } else { perror("Select error"); } close(fd1); return 0; }
内容的提问来源于stack exchange,提问作者user9478215

