与USB转串口适配器通信时,为什么此串行通信代码会挂起?

时间:2012-02-07 15:59:13

标签: objective-c macos cocoa serial-port termios

我正在编写一个应用程序,可以从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] = {'\0','\0','\0'};
    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

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

1 个答案:

答案 0 :(得分:1)

我看到了Prolific的USB转串口适配器驱动程序的严重问题,特别是在Mac OS X Lion下。您可以尝试此alternative driver它基于osx-pl2303开源驱动程序代码。

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

有关此问题的详情,请参阅我的earlier question