Raspberry Pi 的 C 串行通信
Serial Communication in C for Raspberry Pi
我想让两个 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
数据的 post 处理将引发错误。
为了让它工作,我引入了另一个 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 的非常通用的规范串行编程。
注意:规范输入处理还可以处理擦除、删除单词和重印字符,将 CR 翻译为 NL 等。
我建议您阅读这篇文章以了解更多关于串口编程设置,不同模式的信息。
#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; /* '[=10=]' */
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; /* '[=10=]' */
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; /* '[=10=]' */
/*
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;
}
如果您不希望连接是非阻塞的,您可能想要删除您添加到 open
调用中的 O_NDELAY 选项,该选项开启了非阻塞.就像这样...
uart0_filestream = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);
此外,read
不是 return 字符串,它 return 是读入的字节数,因此您对 read
的调用应该更像
bytecount = read(uart0_filestream, str, 20);
if(bytecount>0)
{
str[bytecount]='[=11=]';
}
else
{
// Something bad happened?
}
我想让两个 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
数据的 post 处理将引发错误。
为了让它工作,我引入了另一个 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 的非常通用的规范串行编程。
注意:规范输入处理还可以处理擦除、删除单词和重印字符,将 CR 翻译为 NL 等。
我建议您阅读这篇文章以了解更多关于串口编程设置,不同模式的信息。
#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; /* '[=10=]' */
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; /* '[=10=]' */
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; /* '[=10=]' */
/*
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;
}
如果您不希望连接是非阻塞的,您可能想要删除您添加到 open
调用中的 O_NDELAY 选项,该选项开启了非阻塞.就像这样...
uart0_filestream = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);
此外,read
不是 return 字符串,它 return 是读入的字节数,因此您对 read
的调用应该更像
bytecount = read(uart0_filestream, str, 20);
if(bytecount>0)
{
str[bytecount]='[=11=]';
}
else
{
// Something bad happened?
}