在 c 中使用 unistd.h 在 MAC OSX 上写入串行端口时出现问题

发布于 2024-07-26 03:51:39 字数 3287 浏览 1 评论 0原文

我正在尝试使用 c 中的 unistd.h Linux 函数写入 MAC OSX 上的蓝牙设备。 我连接良好并成功写入前几个字节。 当我尝试向其写入其他命令时(每 15 毫秒就会向写入缓冲区添加一些字节),即使 write() 函数返回 1(写入成功),我也看不到任何结果。

如果您开始一次写入,但在您尝试开始另一次写入时它尚未完成(因为它是非阻塞的),那么这可能会搞砸初始写入吗? (如果是这样,有什么方法可以检查写入是否已完成?)这是我能想到的唯一的事情,因为写入发生得相当频繁,并且前两个已成功发送。

qwbyte() 只是将一个字节添加到输出数组并增加其长度

打开端口函数:

BAMid = -1;
struct termios options;
struct termios originalTTYAttrs;

// Open the serial port read/write, nonblocking, with no controlling terminal, and don't wait for a connection.
BAMid = open(strPath, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (BAMid == -1)
{
    printf("Error opening serial port %s - %s(%d).\n",
           strPath, strerror(errno), errno);
    goto error;
}

// Issue TIOCEXCL ioctl to prevent additional opens except by root-owned processes.
if (ioctl(BAMid, TIOCEXCL) == -1)
{
    printf("Error setting TIOCEXCL on %s - %s(%d).\n",
  strPath, strerror(errno), errno);
    goto error;
}

// Get the current options and save them so we can restore the default settings later.
if (tcgetattr(BAMid, &originalTTYAttrs) == -1)
{
    printf("Error getting tty attributes %s - %s(%d).\n",
  strPath, strerror(errno), errno);
    goto error;
}

// The serial port attributes such as timeouts and baud rate are set by modifying the termios
// structure and then calling tcsetattr() to cause the changes to take effect. Note that the
// changes will not become effective without the tcsetattr() call.

options = originalTTYAttrs;

// Set raw input (non-canonical) mode, with reads blocking until either a single character 
// has been received or a one second timeout expires. [should be moot since we are leaving it as nonblocking]

cfmakeraw(&options);
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 10;
cfsetspeed(&options, B57600);  // Set 57600 baud    
options.c_cflag |= CS8;    // Use 8 bit words

// Cause the new options to take effect immediately.
if (tcsetattr(BAMid, TCSANOW, &options) == -1)
{
    printf("Error setting tty attributes %s - %s(%d).\n",
  strPath, strerror(errno), errno);
    goto error;
}



//flush old transmissions
if (tcflush(BAMid,TCIOFLUSH) == -1) {
    printf("Error flushing BAM serial port - %s(%d).\n",
    strerror(errno), errno);
}  

oBufLength = 0;

// Ask it to start
if (! qwbyte(CmdStart) ) {
    goto error;
}

if (! qwbyte(CmdFull) ) {
    goto error;
} 
//this transmit works
txbytes();

printf("success opening port!");
return -1;
   // Failure path
error:
 if (BAMid != -1) {
        close(BAMid);
    }
 printf("returning an error--%d",errno);
   return errno;
}

写入函数 (txbytes):

int i, bufSize, numBytes;

if(oBufLength != 0) { //if the output array isn't empty

        //duplicating the output array and its size so it can 
        //be overwritten while this write is occuring
        printf("about to transmit: ");
  for(i = 0; i < oBufLength; i++) {
   printf(" %u",oBuf[i]);
   tempBuf[i] = oBuf[i];
  }
  printf("\n");




 bufSize = oBufLength;
 oBufLength = 0; 

 numBytes = write(BAMid, &tempBuf, bufSize);

 printf("bytes written = %d\n",numBytes);

 if (numBytes == -1) {
     printf("Error writing to port - %s(%d).\n", strerror(errno), errno);
 }

 return (numBytes > 0);
}
else {
  return 0;
}

I am trying to write to a bluetooth device on MAC OSX using the unistd.h Linux functions in c. I am connecting fine and writing the first few bytes with success. When I try to write other commands to it (there are bytes added to the write buffer every 15ms), I don't see any results even though the write() function returns 1 (write success).

If you start a write and it doesn't finish by the time you try to start another write (since it is non-blocking), could that possibly screw up the initial write? (If so, is there any way to check if a write has completed?) That is the only thing I can think of since the writes are occurring fairly frequently and the first two are successfully sent.

qwbyte() simply adds a byte to the output array and increments its length

The open port function:

BAMid = -1;
struct termios options;
struct termios originalTTYAttrs;

// Open the serial port read/write, nonblocking, with no controlling terminal, and don't wait for a connection.
BAMid = open(strPath, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (BAMid == -1)
{
    printf("Error opening serial port %s - %s(%d).\n",
           strPath, strerror(errno), errno);
    goto error;
}

// Issue TIOCEXCL ioctl to prevent additional opens except by root-owned processes.
if (ioctl(BAMid, TIOCEXCL) == -1)
{
    printf("Error setting TIOCEXCL on %s - %s(%d).\n",
  strPath, strerror(errno), errno);
    goto error;
}

// Get the current options and save them so we can restore the default settings later.
if (tcgetattr(BAMid, &originalTTYAttrs) == -1)
{
    printf("Error getting tty attributes %s - %s(%d).\n",
  strPath, strerror(errno), errno);
    goto error;
}

// The serial port attributes such as timeouts and baud rate are set by modifying the termios
// structure and then calling tcsetattr() to cause the changes to take effect. Note that the
// changes will not become effective without the tcsetattr() call.

options = originalTTYAttrs;

// Set raw input (non-canonical) mode, with reads blocking until either a single character 
// has been received or a one second timeout expires. [should be moot since we are leaving it as nonblocking]

cfmakeraw(&options);
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 10;
cfsetspeed(&options, B57600);  // Set 57600 baud    
options.c_cflag |= CS8;    // Use 8 bit words

// Cause the new options to take effect immediately.
if (tcsetattr(BAMid, TCSANOW, &options) == -1)
{
    printf("Error setting tty attributes %s - %s(%d).\n",
  strPath, strerror(errno), errno);
    goto error;
}



//flush old transmissions
if (tcflush(BAMid,TCIOFLUSH) == -1) {
    printf("Error flushing BAM serial port - %s(%d).\n",
    strerror(errno), errno);
}  

oBufLength = 0;

// Ask it to start
if (! qwbyte(CmdStart) ) {
    goto error;
}

if (! qwbyte(CmdFull) ) {
    goto error;
} 
//this transmit works
txbytes();

printf("success opening port!");
return -1;
   // Failure path
error:
 if (BAMid != -1) {
        close(BAMid);
    }
 printf("returning an error--%d",errno);
   return errno;
}

The write function (txbytes):

int i, bufSize, numBytes;

if(oBufLength != 0) { //if the output array isn't empty

        //duplicating the output array and its size so it can 
        //be overwritten while this write is occuring
        printf("about to transmit: ");
  for(i = 0; i < oBufLength; i++) {
   printf(" %u",oBuf[i]);
   tempBuf[i] = oBuf[i];
  }
  printf("\n");




 bufSize = oBufLength;
 oBufLength = 0; 

 numBytes = write(BAMid, &tempBuf, bufSize);

 printf("bytes written = %d\n",numBytes);

 if (numBytes == -1) {
     printf("Error writing to port - %s(%d).\n", strerror(errno), errno);
 }

 return (numBytes > 0);
}
else {
  return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

毁我热情 2024-08-02 03:51:39

非阻塞写入不会按照您期望的方式工作。

如果写入无法立即完成,则 write() 返回到您的代码 - 但它不会继续尝试在后台发送数据。 它所做的只是说“我现在无法写作 - 稍后再试”。 它通过返回 -1 来实现此目的,并将 errno 设置为 EAGAIN。

另外,请记住,成功时,write() 返回成功写入的字节数。 因此,如果当您请求写入 2 个字节时得到的返回值为 1,则意味着它仅部分成功,并且您需要再次调用 write() 来写入第二个字节。

基本上,如果您使用非阻塞 IO,您希望 txbytes() 函数在循环中调用 write() 直到缓冲区为空或返回 -1。 如果它返回-1,你需要检查 errno - 如果它是 EAGAIN,你将不得不在其他时间再次调用 write() ; 其他任何事情都可能是一个真正的错误。 像这样的事情:

ssize_t written = 0;

while (oBufLength > 0 && written > -1)
{
    size_t i;

    printf("about to transmit %d bytes: ", oBufLength);
    for(i = 0; i < oBufLength; i++) {
            printf(" %u",oBuf[i]);
    }
    printf("\n");

    written = write(BAMid, oBuf, oBufLength);

    printf("Write returned %d\n", written);

    if (written > 0)
    {
        /* The first "written" number of bytes in the buffer have now been sent, so
         * we discard them and move up the remaining bytes (if any) to the start of 
         * the buffer */

        oBufLength -= written;
        memmove(oBuf, oBuf + written, oBufLength);

        printf("Now have %d bytes left to send.\n", oBufLength);
     }
}

if (written > -1 || errno == EAGAIN)
{
    /* No fatal errors... */
    return 0;
} else
    /* error left in errno for caller to inspect */
    return -1;
}

请注意,无需复制缓冲区 - 因为 write() 不会与您的代码并行执行任何操作。 如果它说它已写入字节,则它不再需要您的缓冲区。 希望有帮助!

Non-blocking writes don't work how you're expecting them to.

If the write can't be completed immediately, write() returns to your code - but it doesn't keep trying to send the data in the background. All it does is say "I couldn't write at the moment - try again later". It does this by returning -1, with errno set to EAGAIN.

Also, remember that when successful, write() returns the number of bytes successfully written. So if you're getting a return value of 1 when you asked for 2 bytes to be written, that means it was only partially successful, and you need to call write() again some time for the second byte.

Basically, if you're using non-blocking IO, you want your txbytes() function to call write() in a loop until either your buffer is empty, or it returns -1. If it returned -1 you need to check errno - if it's EAGAIN, you'll have to call write() again some other time; anything else is probably a real error. Something like this:

ssize_t written = 0;

while (oBufLength > 0 && written > -1)
{
    size_t i;

    printf("about to transmit %d bytes: ", oBufLength);
    for(i = 0; i < oBufLength; i++) {
            printf(" %u",oBuf[i]);
    }
    printf("\n");

    written = write(BAMid, oBuf, oBufLength);

    printf("Write returned %d\n", written);

    if (written > 0)
    {
        /* The first "written" number of bytes in the buffer have now been sent, so
         * we discard them and move up the remaining bytes (if any) to the start of 
         * the buffer */

        oBufLength -= written;
        memmove(oBuf, oBuf + written, oBufLength);

        printf("Now have %d bytes left to send.\n", oBufLength);
     }
}

if (written > -1 || errno == EAGAIN)
{
    /* No fatal errors... */
    return 0;
} else
    /* error left in errno for caller to inspect */
    return -1;
}

Note that there's no need to duplicate the buffer - since write() doesn't do anything in parallel with your code. If it says its written the bytes, it doesn't need your buffer any more. Hope that helps!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文