OK6410-A开发板串口编程总结

在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;
}

好了,串口操作就介绍到这里了。

猜你喜欢

转载自blog.csdn.net/xinxin_2011/article/details/86070226