文章目录
前言
本文介绍了linux上用串口api进行软件开发的步骤,其大致分为4步:(1)打开串口(2)配置串口(3)串口通讯(4)关闭串口
1 打开串口
1.1 代码示例
int libtty_open(const char *devname)
{
int fd = open(devname, O_RDWR | O_NOCTTY | O_NDELAY);
int flags = 0;
if (fd == -1) {
perror("open device failed");
return -1;
}
flags = fcntl(fd, F_GETFL, 0);
flags &= ~O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0) {
printf("fcntl failed.\n");
return -1;
}
if (isatty(fd) == 0)
{
printf("not tty device.\n");
return -1;
}
else
printf("tty device test ok.\n");
return fd;
}
1.2 参数详解
调用open()函数来打开串口,函数执行成功返回句柄,出错返回-1。
函数原型:int open(const char *pathname, int flags);
pathname:表示要打开的串口文件的路径
flag:表示打开方式和读写方式,这里需要设置为O_RDWR| O_NOCTTY| O_NDELAY
调用fcntl()函数来设置串口的访问模式和状态标志。
函数原型:int fcntl(int fd, int cmd, ... /* arg */ );
fd:表示打开的串口文件的句柄
cmd:决定了要进行何种操作,设为F_GETFL时用来获取文件访问模式和状态标志,设为F_SETFL时用来设置文件的文件访问模式和状态标志
arg :arg的取值与cmd的设定值有关,当cmd取值为F_GETFL时,arg的值可以忽略,当cmd取值为F_SETFL时,arg为对文件访问模式和状态标志的设定值。先将cmd设为F_GETFL调用该函数,返回值为当前串口文件的访问模式和状态标志,然后将获取到的文件访问模式和状态标志进行修改,再将cmd设置为F_SETFL调用该函数,将修改后的状态标志写入arg来对串口文件进行设置。
调用isatty()函数来测试打开的文件是否指向一个终端,返回1表示指向一个终端,返回0表示未指向一个终端
函数原型:int isatty(int fd);
fd:打开的串口文件的句柄
2 配置串口
2.1 代码示例
int libtty_setopt(int fd, int speed, int databits, int stopbits, char parity, char hardflow)
{
struct termios newtio;
struct termios oldtio;
int i;
bzero(&newtio, sizeof(newtio));
bzero(&oldtio, sizeof(oldtio));
if (tcgetattr(fd, &oldtio) != 0) {
perror("tcgetattr");
return -1;
}
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
/* set tty speed */
for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {
if (speed == name_arr[i]) {
cfsetispeed(&newtio, speed_arr[i]);
cfsetospeed(&newtio, speed_arr[i]);
}
}
/* set data bits */
switch (databits) {
case 5:
newtio.c_cflag |= CS5;
break;
case 6:
newtio.c_cflag |= CS6;
break;
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
default:
fprintf(stderr, "unsupported data size\n");
return -1;
}
/* set parity */
switch (parity) {
case 'n':
case 'N':
newtio.c_cflag &= ~PARENB; /* Clear parity enable */
newtio.c_iflag &= ~INPCK; /* Disable input parity check */
break;
case 'o':
case 'O':
newtio.c_cflag |= (PARODD | PARENB); /* Odd parity instead of even */
newtio.c_iflag |= INPCK; /* Enable input parity check */
break;
case 'e':
case 'E':
newtio.c_cflag |= PARENB; /* Enable parity */
newtio.c_cflag &= ~PARODD; /* Even parity instead of odd */
newtio.c_iflag |= INPCK; /* Enable input parity check */
break;
default:
fprintf(stderr, "unsupported parity\n");
return -1;
}
/* set stop bits */
switch (stopbits) {
case 1:
newtio.c_cflag &= ~CSTOPB;
break;
case 2:
newtio.c_cflag |= CSTOPB;
break;
default:
perror("unsupported stop bits\n");
return -1;
}
if (hardflow)
newtio.c_cflag |= CRTSCTS;
else
newtio.c_cflag &= ~CRTSCTS;
newtio.c_cc[VTIME] = 0; /* Time-out value (tenths of a second) [!ICANON]. */
newtio.c_cc[VMIN] = 0; /* Minimum number of bytes read at once [!ICANON]. */
tcflush(fd, TCIOFLUSH);
if (tcsetattr(fd, TCSANOW, &newtio) != 0)
{
perror("tcsetattr");
return -1;
}
return 0;
}
int libtty_tiocmget(int fd)
{
unsigned long modembits = 0;
int ret;
ret = ioctl(fd, TIOCMGET, &modembits);
if (modembits & TIOCM_DSR) //表示modem方式为DSR
printf("DSR Active!\n");
if (modembits & TIOCM_CTS) //表示modem方式为CTS
printf("CTS Active!\n");
if (modembits & TIOCM_CD) //表示modem方式为CD
printf("DCD Active!\n");
if (modembits & TIOCM_RI) //表示modem方式为RI
printf("RI Active!\n");
if (ret)
return ret;
else
return modembits;
}
int libtty_tiocmset(int fd, char bDTR, char bRTS)
{
unsigned long controlbits = 0;
if (bDTR)
controlbits |= TIOCM_DTR; //设置modem状态为DTR
if (bRTS)
controlbits |= TIOCM_RTS; //设置modem状态为RTS
return ioctl(fd, TIOCMSET, &controlbits);
}
2.2 参数详解
首先要创建一个newtio结构体并清空,然后才可以通过将串口的一些配置参数填入newtio结构体。
2.2.1 忽略modem控制线
然后通过更改newtio结构体中的c_cflag来忽略modem控制线
newtio.c_cflag |= CLOCAL; //CLOCAL参数的作用是忽略modem控制线
2.2.2 使能串口接收
newtio.c_cflag |= CREAD; //CREAD参数的作用是使能串口接收
2.2.3 配置串口波特率
我们可以使用cfsetispeed()和cfsetosspeed()来分别配置串口的输入输出波特率
函数原型:
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);
termios_p是存放串口配置的结构体的指针
speed的值为期望设定的波特率
2.2.4 设置数据位长度
在设置数据位长度之前先清空newtio结构体的c_cflag中的数据位长度设置,
然后通过修改c_cflag来修改修改数据位长度。
newtio.c_cflag &= ~CSIZE; //CSIZE是用来设置数据位长度的, &= ~CSIZE表示清空数据位
newtio.c_cflag |= CS5; //设置数据位长度为5
newtio.c_cflag |= CS6; //设置数据位长度为6
newtio.c_cflag |= CS7; //设置数据位长度为7
newtio.c_cflag |= CS8; //设置数据位长度为8
2.2.5 设置校验方式
我们可以修改newtio结构体的c_cflag和c_iflag来设置校验方式
需要使用到三个参数:
PARENB:用来使能校验
INPCK:用来使能输入校验检查
PAROOD:用来设定校验方式为奇校验,反之为偶校验
2.2.6 设置停止位
通过设置结构体的c_cflag来设置停止位
需要使用到一个参数:
CSTOPB:用来设置为2个校验位,反之为一个校验位
2.2.7 设置流控
通过设置newtio结构体的c_cflag来设置停止位
需要使用到一个参数:
CRTSCTS:用来使能流控,反之清除流控使能
2.2.8 设置最小读取字节数和超时时间
通过修改newtio结构体中的c_cc[VTIME]和c_cc[VMIN]来修改最小读取字节和超时时间
可以分为四类情况:
(1)MIN=0,TIME=0(轮训读,即不用等待,直接读)
如果数据可获取,read立刻返回,读取可获得的最少字节数或请求的字节数,如果没有可以获得的数据,则返回0
(2)MIN>0,TIME=0(阻塞读)
Read直到可以读到MIN数量的字节数可以读到才返回
(3)MIN=0,TIME>0
TIME的值以1/10秒为单位,计时开始于read(2)被调用,当至少读到1个字节或TIME时间期满时,read才会被返回,如果时间期满没有任何可以获得的数据,则read返回0。
如果调用read的时候已经有可获得的数据,则调用的行为就像是在调用后立刻接收了数据。
(4)MIN>0,TIME>0
与MIN=0,TIME>0不同的是,MIN>0,TIME>0的情况下timer是用来对字节间进行计时的,而MIN==0,TIME>0的情况下timer是用来对每次read进行计时的,一旦第一个字节是可获得的,在接收到之后的每一个字节后,定时器将会重启,在以下三种情况的任何一个满足的时候,read将会返回。
a)接收到了最小字节数
b)字节间的时间间隔达到了TIME
c)read请求的字节数已经被接收完了
2.2.9 清空读写缓存
调用tcflush()函数来清空读写缓存
函数原型:int tcflush(int fd, int queue_selector);
fd:打开的串口文件的句柄
queue_selector:用来选择清空什么缓存,可选的值有TCIFLUSH、TCOFLUSH、TCIOFLUSH,分别表示清空读缓存、清空写缓存、清空读写缓存,这里使用TCIOFLUSH
2.2.10 获取modem状态
可以根据需求获取modem状态
函数int libtty_tiocmget(int fd)是用来获取modem状态的
其中使用了ioctl()函数来获取modem状态
函数原型:int ioctl(int fd, unsigned long request, …);
fd:打开的串口文件的句柄
request:程序对串口驱动发的命令,这里使用TIOCMGET,表示获取modem状态
…:该参数的意义由参数request决定,这里的该参数指向的内存用来存放从驱动发回来的值,以下4个值对应四个modem状态
TIOCM_DSR:表示获取到的modem状态为DSR
TIOCM_CTS:表示获取到的modem状态为CTS
TIOCM_CD:表示获取到的modem状态为CD
TIOCM_RI:表示获取到的modem状态为RI
2.2.11 设置modem状态
可以根据需求来设置modem状态
函数int libtty_tiocmset(int fd, char bDTR, char bRTS)是用来设置modem状态的
其中使用了ioctl()函数来设置modem状态
函数原型:int ioctl(int fd, unsigned long request, …);
fd:打开的串口文件的句柄
request:程序对串口驱动发的命令,这里使用TIOCMSET,表示设置modem状态
…:该参数的意义由参数request决定,这里的该参数指向的内存用来存放想要设置的modem状态的执,以下2个值对应2个modem状态
TIOCM_DTR:表示设置modem状态为DTR
TIOCM_RTS:表示设置modem状态为RTS
3 读写串口
3.1 代码示例
void tty_test(int fd)
{
int nwrite, nread;
char buf[100];
int i;
memset(buf, 0x32, sizeof(buf));
nwrite = write(fd, buf, sizeof(buf));
printf("wrote %d bytes already.\n", nwrite);
sleep(1);
nread = read(fd, buf, sizeof(buf));
printf("read %d bytes already.\n", nread);
printf("*************************\n");
for (i = 0; i < nread; i++)
printf(" 0x%.2x", buf[i]);
printf("\n*************************\n");
sleep(1);
}
3.2 参数详解
完成配置后可以调用read()和write()对串口进行读写
函数原型:ssize_t read(int fd, void *buf, size_t count);
fd:串口文件句柄
buf:读缓冲指针
count:期望读取的字节数
返回值:成功返回读到的字节数,出错返回-1
函数原型:ssize_t write(int fd, const void *buf, size_t count);
fd:串口文件句柄
buf:写缓冲指针
count:期望写入的字节数
返回值:成功返回写入的字节数,出错返回-1
4 关闭串口
4.1 代码示例
int libtty_close(int fd)
{
return close(fd);
}
4.2 参数详解
调用close()函数来关闭串口,函数执行成功返回0,出错返回-1。
函数原型:int close(int fd);
fd:打开的串口文件的句柄