为什么在与USB到串行适配器通话时,此串行通信代码会挂起



我正在编写一个应用程序,该应用程序应该从Mac Mini的桌面控制投影仪。我正在使用Xcode和termios.h在Objective-C中编写应用程序,以与串行端口通信。我正在从USB连接到串行适配器,我知道驱动程序安装正确,因为我可以向投影仪发送HEX命令来控制其功能。

我写了一些代码,我认为应该打开串行端口,连接到投影仪,发送代码,然后断开连接:

@implementation Projector
@synthesize helpUrl = _helpUrl;
@synthesize projectorConnected;
- (id)init {
    //[self connectProjector];
    if (TRUE == projectorConnected) {
        // Success!
    }
    else {
        // Error!
    }
    return self;
}
- (id)initWithHelpUrl:(NSString *)helpUrlString {
    [self setHelpUrl:helpUrlString];
    return [self init];
}
- (void)connectProjector {
    [self setProjectorConnected:FALSE];
    NSString *deviceNameString = @"/dev/tty.usbserial";
    speed_t baudRate = B19200;
    serialFileDescriptor = [self openSerialPort:[deviceNameString cStringUsingEncoding:NSASCIIStringEncoding] baud:baudRate];
    if (serialFileDescriptor == -1) {
        NSLog(@"Error opening serial port file!");
    }
    _fileHandle = [[NSFileHandle alloc] initWithFileDescriptor: serialFileDescriptor];
    //[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveSerialMessage:) name:NSFileHandleReadCompletionNotification object:_fileHandle];
    //[_fileHandle readInBackgroundAndNotify];
    // [self closeSerialPort];
}
- (int)openSerialPort:(const char *)serialPortFile baud:(speed_t)baudRate {
    struct termios toptions;
    int fd;
    fd = open(serialPortFile, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1)  {
        perror("init_serialport: Unable to open port ");
        return -1;
    }
    if (tcgetattr(fd, &gOriginalTTYAttrs) < 0) {
        perror("init_serialport: Couldn't get term attributes");
        return -1;
    }
    toptions = gOriginalTTYAttrs;
    cfsetispeed(&toptions, baudRate);
    cfsetospeed(&toptions, baudRate);
    toptions.c_cflag &= PARENB;
    toptions.c_cflag &= CSTOPB;
    toptions.c_cflag &= CSIZE;
    toptions.c_cflag |= CS8;
    toptions.c_cflag &= CRTSCTS;
    toptions.c_cflag |= CREAD | CLOCAL;  
    toptions.c_iflag &= (IXON | IXOFF | IXANY); 
    toptions.c_lflag &= (ICANON | ECHO | ECHOE | ISIG); 
    toptions.c_oflag &= OPOST; 
    // see: http://unixwiz.net/techtips/termios-vmin-vtime.html
    toptions.c_cc[VMIN]  = 0;
    toptions.c_cc[VTIME] = 20;
    if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
        perror("init_serialport: Couldn't set term attributes");
        return -1;
    }
    return fd;
}
- (void)sendSerialMessage:(NSString *)message {
    NSString *deviceNameString = @"/dev/tty.usbserial";
    speed_t baudRate = B19200;
    serialFileDescriptor = [self openSerialPort:[deviceNameString cStringUsingEncoding:NSASCIIStringEncoding] baud:baudRate];
    if (serialFileDescriptor == -1) {
        NSLog(@"Error opening serial port file!");
    }
    _fileHandle = [[NSFileHandle alloc] initWithFileDescriptor: serialFileDescriptor];
    //[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveSerialMessage:) name:NSFileHandleReadCompletionNotification object:_fileHandle];
    //[_fileHandle readInBackgroundAndNotify];
    // convert message string into NSData
    NSString *dataString = [message stringByReplacingOccurrencesOfString:@" " withString:@""];
    NSMutableData *dataToSend = [[NSMutableData alloc] init];
    unsigned char whole_byte;
    char byte_chars[3] = {'','',''};
    int i;
    for (i=0; i < 6; i++) {
        byte_chars[0] = [dataString characterAtIndex:i*2];
        byte_chars[1] = [dataString characterAtIndex:i*2+1];
        whole_byte = strtol(byte_chars, NULL, 16);
        [dataToSend appendBytes:&whole_byte length:1];
    }
    // write to the serial port file
    [_fileHandle writeData:dataToSend];
    sleep(10);
    [self closeSerialPort];
}
- (void)receiveSerialMessage:(NSNotification *)notification {
    NSData* messageData = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
    if ( [messageData length] == 0 ) {
        [_fileHandle readInBackgroundAndNotify];
        return;
    }
    NSString* receivedMessage = [[NSString alloc] initWithData:messageData encoding:NSASCIIStringEncoding];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"serialMessageReceived" object:receivedMessage];
    [_fileHandle readInBackgroundAndNotify];
}
// Given the file descriptor for a serial device, close that device.
- (void)closeSerialPort {
    ///*
    // Block until all written output has been sent from the device.
    // Note that this call is simply passed on to the serial device driver. 
    // See tcsendbreak(3) ("man 3 tcsendbreak") for details.
    if (tcdrain(serialFileDescriptor) == -1) {
        NSLog(@"Error waiting for drain - %s(%d).", strerror(errno), errno);
    }
    // Traditionally it is good practice to reset a serial port back to
    // the state in which you found it. This is why the original termios struct
    // was saved.
    if (tcsetattr(serialFileDescriptor, TCSANOW, &gOriginalTTYAttrs) == -1) {
        NSLog(@"Error resetting tty attributes - %s(%d).n", strerror(errno), errno);
    }
    //*/
    close(serialFileDescriptor);
    serialFileDescriptor = -1;
    _fileHandle = nil;
}
- (BOOL)powerOn {
    [self sendSerialMessage:@"02 00 00 00 00 02"];
    // TODO: change to success/failure
    return TRUE;
}
- (BOOL)powerOff {
    [self sendSerialMessage:@"02 01 00 00 00 03"];
    // TODO: change to success/failure
    return TRUE;
}
- (BOOL)blankScreen {
    [self sendSerialMessage:@"02 10 00 00 00 12"];
    // TODO: change to success/failure
    return TRUE;
}
- (BOOL)showScreen {
    [self sendSerialMessage:@"02 11 00 00 00 13"];
    // TODO: change to success/failure
    return TRUE;
}
- (BOOL)requestHelp {
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:_helpUrl]];
    // Perform request and get data back
    NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
    if (nil != responseData) {
        // TODO change this to check for response code is 200?
        return TRUE;
        /*
         // get String value of response
         NSString *returnString = [[NSString alloc] initWithData:responseData            encoding:NSUTF8StringEncoding];
         // DEBUG
         NSLog(@"Return string: %@", returnString);
         // TODO: better way to do this?
         NSString *successString = @"{"status":true}";
         if ([returnString isEqualToString:successString]) {
         // success
         NSLog(@"Success!");
         return TRUE;
         }
         else {
         NSLog(@"Error bad response");
         }
         */
    }
    else {
        NSLog(@"Error: no response");
    }
    // return faliure!
    return FALSE;
}
@end

我想知道哪里出了问题,因为当我运行程序时,它挂起了。

我看到Prolific的USB到串行适配器驱动程序出现了严重问题,尤其是在Mac OS X Lion下。你可以试试这个替代驱动程序。它基于osx-pl2303开源驱动程序代码。

就我个人而言,我建议使用不同的USB转串行适配器。我喜欢Keyspan的USA-19HS适配器,在任何版本的Mac OS X上,他们的驱动程序都没有问题。我也听说过FTDI的好消息,但对基于该芯片组的适配器没有个人经验。

有关此问题的详细信息,请参阅我前面的问题。

最新更新