【问题标题】:Serial Communication in C for Raspberry PiRaspberry Pi 用 C 语言进行串行通信
【发布时间】:2018-08-01 16:54:45
【问题描述】:

我想让两个 Raspberry Pi 使用 ZigBee 协议互相发送消息。 我已经使用 USB Explorer (CH430g) 将 XBee S2C (ZigBee) 模块连接到 Raspberry Pi。 我已经编写了一个 python 脚本来完成所需的工作,

import serial

ser = serial.Serial('/dev/ttyUSB0', 9600)

while True:
    incoming = ser.readline().strip()
    print ('%s' %incoming.decode())
    string = input("") + '\n'
    ser.write(string.encode())

但是我需要一个 C 程序来做同样的事情, 我查看了用于 C 和 C++ 的 libserial 库,发现它有问题并且从未为我编译过。

我试过this thread 它工作得很好,但是在接收方我需要保持read(fd, &buffer, sizeof(buffer));while 循环中以持续打开以进行侦听,这与C 套接字程序不同,其中read() 函数将停止直到它接收数据就像我的 python 脚本将在行中等待incoming = ser.readline().strip() 直到它收到一些消息。

不使用while循环有什么解决办法吗?

编辑 1:

在上述python代码中,while循环用于接收多条消息。 incoming = ser.readline().strip() 行将捕获消息,对其进行处理并等待下一条消息,因为它在 while 循环中。

如果我的代码是这样的,在 C 中:

while(1){
    str = read();
    //some processing
    }

它会抛出错误,因为read 在获取数据之前不会停止,它只是返回读取失败。由于读取的数据是NULL,数据的后处理会抛出错误。 为了让它发挥作用,我引入了另一个 while 循环,如下所示:

 while(1){
    while(1){
        str = read();
        if(str!=NULL)
            break;
        }
    //some processing
    }

我想消除这个额外的循环并让read() 等待消息。 PS:我打开串口设备是这样的:uart0_filestream = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);

【问题讨论】:

  • 如果您专门针对 C 提出这个问题,为什么是 C++?
  • 上述线程中的read()阻塞,会使进程进入睡眠状态等待数据,所以是的,你可以在循环中使用它(小心&buffer,是bug,也看答案)。我可以问一下为什么如果 python 脚本正在运行,你需要在 C 中使用它吗?
  • @hellow 我更喜欢 C,但 C++ 中提供了解决方案,我对此很好。
  • @Alex 其实我不想在 while 循环中使用read(),因为代码只是其他大型项目的一部分。所以我在问如何避免 while 循环或任何可用于 C/C++ 的库。正如我所说,它是项目的一部分,应该是 C/C++ 我不能在其中使用 python 脚本。
  • @VinayakaSP 我现在看到了,对不起,你可以将串口配置为非阻塞,你可以在等待数据的同时使用select()做一些其他的事情,但是如果您期望更多数据,我看不出如何摆脱循环

标签: c raspberry-pi serial-communication


【解决方案1】:

如果您不希望连接是非阻塞的,您可能想要删除您在调用open 时添加的启用非阻塞的 O_NDELAY 选项。就这样……

uart0_filestream = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);

另外,read 不返回字符串,它返回读入的字节数,因此您对 read 的调用应该看起来更像

bytecount = read(uart0_filestream, str, 20);
if(bytecount>0)
   {
   str[bytecount]='\0';
   }
else
   {
   // Something bad happened?
   }

【讨论】:

  • 谢谢!看来这个解决方案已经解决了我的问题!
【解决方案2】:

从这段代码中获得一些灵感。这是使用 C 的非常通用的规范串行编程。

注意:规范输入处理还可以处理擦除、删除单词和重印字符,将 CR 转换为 NL 等。


我建议你阅读这篇文章以了解更多关于串口编程设置、不同模式的信息。

HowTo serial programming.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>

/* baudrate settings are defined in <asm/termbits.h>, which is
included by <termios.h> */
#define BAUDRATE B38400            
/* change this definition for the correct port */
#define SERIAL_DEVICE "/dev/ttyS1"


#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE; 

int main(void)
{
  int fd,c, res;
  struct termios oldtio,newtio;
  char buf[255];
/* 
  Open modem device for reading and writing and not as controlling tty
  because we don't want to get killed if linenoise sends CTRL-C.
*/
 fd = open(SERIAL_DEVICE, O_RDWR | O_NOCTTY ); 
 if (fd <0) {perror(SERIAL_DEVICE); exit(-1); }

 tcgetattr(fd,&oldtio); /* save current serial port settings */
 memset(&newtio, sizeof(newtio)); /* clear struct for new port settings */

/* 
  BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
  CRTSCTS : output hardware flow control (only used if the cable has
            all necessary lines. See sect. 7 of Serial-HOWTO)
  CS8     : 8n1 (8bit,no parity,1 stopbit)
  CLOCAL  : local connection, no modem contol
  CREAD   : enable receiving characters
*/
 newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

/*
  IGNPAR  : ignore bytes with parity errors
  ICRNL   : map CR to NL (otherwise a CR input on the other computer
            will not terminate input)
  otherwise make device raw (no other input processing)
*/
 newtio.c_iflag = IGNPAR | ICRNL;

/*
 Raw output.
*/
 newtio.c_oflag = 0;

/*
  ICANON  : enable canonical input
  disable all echo functionality, and don't send signals to calling program
*/
 newtio.c_lflag = ICANON;

/* 
  initialize all control characters 
  default values can be found in /usr/include/termios.h, and are given
  in the comments, but we don't need them here
*/
 newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */ 
 newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
 newtio.c_cc[VERASE]   = 0;     /* del */
 newtio.c_cc[VKILL]    = 0;     /* @ */
 newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */
 newtio.c_cc[VTIME]    = 0;     /* inter-character timer unused */
 newtio.c_cc[VMIN]     = 1;     /* blocking read until 1 character arrives */
 newtio.c_cc[VSWTC]    = 0;     /* '\0' */
 newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */ 
 newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
 newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
 newtio.c_cc[VEOL]     = 0;     /* '\0' */
 newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
 newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
 newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
 newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
 newtio.c_cc[VEOL2]    = 0;     /* '\0' */

/* 
  now clean the modem line and activate the settings for the port
*/
 tcflush(fd, TCIFLUSH);
 tcsetattr(fd,TCSANOW,&newtio);

/*
  terminal settings done, now handle input
  In this example, inputting a 'z' at the beginning of a line will 
  exit the program.
*/
 while (STOP==FALSE) {     /* loop until we have a terminating condition */
 /* read blocks program execution until a line terminating character is 
    input, even if more than 255 chars are input. If the number
    of characters read is smaller than the number of chars available,
    subsequent reads will return the remaining chars. res will be set
    to the actual number of characters actually read */
    res = read(fd,buf,255); 
    buf[res]=0;             /* set end of string, so we can printf */
    printf(":%s:%d\n", buf, res);
    if (buf[0]=='z') STOP=TRUE;
 }
 /* restore the old port settings */
 tcsetattr(fd,TCSANOW,&oldtio);
 return 0;
}

【讨论】:

  • 感谢您的建议,我一定会通过它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-27
  • 1970-01-01
  • 2019-05-27
相关资源
最近更新 更多