在Linux下设备操作跟文件操作是相似的,基本步骤无非就是打开,读写,关闭。首先包含上基本的头文件:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
先来看打开串口;
int open_port(int comport)
{
int fd;
if (comport==0)
{
fd = open( "/dev/ttySAC0", O_RDWR|O_NOCTTY|O_NDELAY);
if (-1 == fd)
{
perror("Can't Open Serial Port");
return(-1);
}
else
{
printf("open Port0 .....\n");
}
}
if (comport==1)
{
fd = open( "/dev/ttySAC1", O_RDWR|O_NOCTTY|O_NDELAY);
if (-1 == fd)
{
perror("Can't Open Serial Port");
return(-1);
}
else
{
printf("open Port1.....\n");
}
}
printf("fd-open=%d\n",fd);
return fd;
}
open函数第一个变量对应的是设备节点,这个是跟驱动相关的,OK6410-A板的串口被识别为/dev/ttySACx(x为0~3)。如果是其他的嵌入式Linux系统,也可能是/dev/ttyUSB0,一定要写正确。
打开成功后就可以进行发送了:
write(fd, "hello", 5);
这里的fd就是刚才打开时返回的fd,hello长度为5,所以发送5个字节,是不是很简单啊。同样接收也简单:
unsigned char recv_buf[1024];
int nread = read(fd, recv_buf, 5);
这里的5表示要读取5个字节,而返回的nread是实际读取到的字节数。如果不知道该读取多少个字节怎么办呢?最简单的循环读取吧:
unsigned char temp;
while(1)
{
int nread = read(fd, temp, 1);
…
if (…)
break;
}
这样一个个字节循环读取,直到接收到我们需要的字节,或者超时什么的,跳出循环。使用完毕后,别忘了关闭设备:
close(fd);
这个最简单,当然上面只是说了最基本的操作函数,实际使用的时候当然要进行各种配置和处理。比如打开串口后的处理:
// 恢复串口为阻塞状态,这样read函数不会立即返回
if(fcntl(fd, F_SETFL, 0) < 0)
{
printf("fcntl set failed!\n");
return(-2);
}
// 测试是否为终端设备,这个需要包含头文件<unistd.h>
if (0 == isatty(STDIN_FILENO))
{
printf("this port is not a terminal device\n");
return(-3);
}
// 清空输入输出缓存
tcflush(fd, TCIOFLUSH);
实际使用串口通讯,肯定要先设置波特率,奇偶校验和停止位,一般设置流程是先读取当前的设置状态,在此基础上设置新的标志。提到这些配置,就有必要先说明一下struct termio和struct termios结构体,两个结构体内容是一致的,只不过struct termios是符合POSIX标准的系统,为了和之前的版本区分而添加了个s,包括的头文件跟名字是相对应的:
#include <termio.h>
#include <termios.h>
虽然结构体内容一样,但操作的方式却不同,如果用struct termio,获取当前状态的方式:
struct termio term_attr;
/* Get current setting */
if (ioctl(fd, TCGETA, &term_attr) < 0)
{
return -1;
}
而如果用struct termios,需要换为tcgetattr函数:
struct termios term_attr;
if (tcgetattr(fd, & term_attr) !=0)
{
return -1;
}
先来设置波特率:
int anSpeedArr[] = { B115200, B57600, B38400, B19200, B9600, B4800, B2400};
unsigned long audwNameArr[] = { 115200, 57600, 38400, 19200, 9600, 4800, 2400};
int ret;
for (i = 0; i < sizeof(anSpeedArr) / sizeof(int); i++)
{
if(udwSpeed == audwNameArr[i])
{
term_attr.c_cflag |= anSpeedArr[i];
ret = ioctl(fd, TCSETAW, &term_attr);
if(ret < 0)
{
printf("Set serial speed Tcsetattr failed(%d).\r\n", ret);
return;
}
tcflush(fd, TCIOFLUSH);
return;
}
}
如果使用struct termios代码如下:
int anSpeedArr[] = { B115200, B57600, B38400, B19200, B9600, B4800, B2400};
unsigned long audwNameArr[] = { 115200, 57600, 38400, 19200, 9600, 4800, 2400};
int ret;
for (i = 0; i < sizeof(anSpeedArr) / sizeof(int); i++)
{
if(udwSpeed == audwNameArr[i])
{
cfsetispeed(&term_attr, anSpeedArr[i]);
cfsetospeed(&term_attr, anSpeedArr[i]);
ret = tcsetattr(fd, TCSANOW, & term_attr);
if(ret!= 0)
{
printf("Set serial speed Tcsetattr failed(%d).\r\n", ret);
return;
}
tcflush(fd, TCIOFLUSH);
return;
}
}
设置数据位数,支持5、6、7、8:
/* Set databits */
term_attr.c_cflag &= ~(CSIZE); // 先屏蔽其他标志位
switch (databits)
{
case 5:
term_attr.c_cflag |= CS5;
break;
case 6:
term_attr.c_cflag |= CS6;
break;
case 7:
term_attr.c_cflag |= CS7;
break;
case 8:
default:
term_attr.c_cflag |= CS8;
break;
}
设置奇偶校验:
/* Set parity */
switch (parity) {
case 1: /* 奇校验 */
term_attr.c_cflag |= (PARENB | PARODD);
break;
case 2: /* 偶校验 */
term_attr.c_cflag |= PARENB;
term_attr.c_cflag &= ~(PARODD);
break;
case 0: /* 无校验 */
default:
term_attr.c_cflag &= ~(PARENB);
break;
}
设置停止位:
/* Set stopbits */
switch (stopbits) {
case 2: /* 2 停止位 */
term_attr.c_cflag |= CSTOPB;
break;
case 1: /* 1 停止位 */
default:
term_attr.c_cflag &= ~CSTOPB;
break;
}
设置接收时间:
term_attr.c_cc[VTIME] = 1; // 读取一个字符等待1*(1/10)s
term_attr.c_cc[VMIN] = 1; // 读取字符的最少个数为1
这里需要说明的是VMIN,如果将其赋值为0的话,read()函数就变为非阻塞了,读取不到数据就直接返回,读取超时就需要我们自己设定了。
以下是一些其他配置设置:
// 修改控制模式,忽略 modem 控制线
term_attr.c_cflag |= CLOCAL;
// 修改控制模式,使能从串口中读取输入数据
term_attr.c_cflag |= CREAD;
// 修改输出模式,原始数据输出
term_attr.c_oflag &= ~OPOST;
term_attr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
其他详细的设置说明请参见这篇博文: https://blog.csdn.net/yemingzhu163/article/details/5897156 里面写得很详细了。
设置完成后记得要使设置生效啊:
if (ioctl(fd, TCSETAW, &term_attr) < 0)
{
return -1;
}
对于使用struct termios结构体:
if (tcsetattr(fd, TCSANOW, & term_attr) != 0)
{
return -1;
}
好了,串口操作就介绍到这里了。