Linux系统编程7-串口通信

序号内容链接
1多进程点我访问
2进程间通信点我访问
3多线程点我访问
4网络编程点我访问
5shell点我访问
6Makefile点我访问
7串口通信点我访问
8I2C通信点我访问

一 Linux下代码控制串口

Linux下把串口当成控制台对待,想对串口操作,其实就是对控制台操作;
Linux下有专门的串口驱动节点,通过系统io操作,这些节点的名字都有如下规律:
/dev/ttyS*
/dev/ttyAMA*
/dev/ttyO*
/dev/ttySAC*

至于你的开发板的串口驱动节点是哪一种名字,不同的开发板是不同的,所以要根据供货商提供的操作手册进行操作;
在这里插入图片描述
操作串口的方法:
系统IO -> open,read,write,close…
能用标准IO操作串口吗 -> 不能,因为串口是字符设备,标准io是以块为单位来传输的,不是以字符为单位传输的,所以不行。

1.1 判断驱动节点是否是串口设备

if(tcgetattr(fd, &oldtio) != 0){ //判断你的驱动节点是不是一个串口设备,假如不反回0,就不是串口设备
return -1;
}

1.2 struct termios详解

struct termios

{
unsigned short c_iflag; // 输入模式标志
unsigned short c_oflag; // 输出模式标志
unsigned short c_cflag; // 控制模式标志
unsigned short c_lflag; //区域模式标志或本地模式标志或局部模式
unsigned char c_line; //行控制line discipline
unsigned char c_cc[NCC]; // 控制字符特性
};

c_oflag参数

键 值说 明
OPOST处理后输出
OLCUC将输出的小写字符转换成大写字符(非POSIX)
ONLCR将输出的NL(换行)转换成CR(回车)及NL(换行)
OCRNL将输出的CR(回车)转换成NL(换行)
ONOCR第一行不输出回车符
ONLRET不输出回车符
OFILL发送填充字符以延迟终端输出
OFDEL以ASCII码的DEL作为填充字符,如果未设置该参数,填充字符为NULL
NLDLY换行输出延时,可以取NL0(不延迟)或NL1(延迟0.1s)
CRDLY回车延迟,取值范围为:CR0、CR1、CR2和 CR3
TABDLY水平制表符输出延迟,取值范围为:TAB0、TAB1、TAB2和TAB3
BSDLY空格输出延迟,可以取BS0或BS1
VTDLY垂直制表符输出延迟,可以取VT0或VT1
FFDLY换页延迟,可以取FF0或FF1
c_cflag参数
键 值说 明
CBAUD波特率(4+1位)(非POSIX)
CBAUDEX附加波特率(1位)(非POSIX)
CSIZE字符长度,取值范围为CS5、CS6、CS7或CS8
CSTOPB设置两个停止位
CREAD使用接收器
PARENB使用奇偶校验
PARODD对输入使用奇偶校验,对输出使用偶校验
HUPCL关闭设备时挂起
CLOCAL忽略调制解调器线路状态
CRTSCTS使用RTS/CTS流控制

c_lflag参数

键 值说 明
ISIG当输入INTR、QUIT、SUSP或DSUSP时,产生相应的信号
ICANON使用标准输入模式
XCASE在ICANON和XCASE同时设置的情况下,终端只使用大写。
ECHO显示输入字符
ECHOE如果ICANON同时设置,ERASE将删除输入的字符
ECHOK如果ICANON同时设置,KILL将删除当前行
ECHONL如果ICANON同时设置,即使ECHO没有设置依然显示换行符
ECHOPRT如果ECHO和ICANON同时设置,将删除打印出的字符(非POSIX)
TOSTOP向后台输出发送SIGTTOU信号

c_cc支持的控制字符

说 明说 明
VINTRInterrupt字符VEOL附加的End-of-file字符
VQUITQuit字符VTIME非规范模式读取时的超时时间
VERASEErase字符VSTOPStop字符
VKILLKill字符VSTARTStart字符
VEOFEnd-of-file字符VSUSPSuspend字符
VMIN非规范模式读取时的最小字符数

1.3 设置波特率

static speed_t getBaudrate(int baudrate)
{
	switch(baudrate) {
		case 0: 		return B0;
		case 50: 		return B50;
		case 75: 		return B75;
		case 110: 		return B110;
		case 134: 		return B134;
		case 150: 		return B150;
		case 200: 		return B200;
		case 300: 		return B300;
		case 600: 		return B600;
		case 1200: 		return B1200;
		case 1800: 		return B1800;
		case 2400: 		return B2400;
		case 4800: 		return B4800;
		case 9600: 		return B9600;
		case 19200: 	return B19200;
		case 38400: 	return B38400;
		case 57600: 	return B57600;
		case 115200: 	return B115200;
		case 230400: 	return B230400;
		case 460800: 	return B460800;
		case 500000: 	return B500000;
		case 576000: 	return B576000;
		case 921600: 	return B921600;
		case 1000000: 	return B1000000;
		case 1152000: 	return B1152000;
		case 1500000: 	return B1500000;
		case 2000000: 	return B2000000;
		case 2500000: 	return B2500000;
		case 3000000: 	return B3000000;
		case 3500000: 	return B3500000;
		case 4000000: 	return B4000000;
		default: 		return -1;
	}
}
	struct termios newtio,oldtio;
		/*struct termios
	{
		unsigned short c_iflag; // 输入模式标志
		unsigned short c_oflag; // 输出模式标志
		unsigned short c_cflag; // 控制模式标志
		unsigned short c_lflag; //区域模式标志或本地模式标志或局部模式
		unsigned char c_line; //行控制line discipline 
		unsigned char c_cc[NCC]; // 控制字符特性
	}*/	
	speed_t bSpeed = B115200;
	bSpeed = getBaudrate(nSpeed);
	/* 设置输入波特率 */
	cfsetispeed(&newtio, bSpeed);
	/* 设置输出波特率 */
	cfsetospeed(&newtio, bSpeed);

1.4 数据位

/* 设置数据位 */
switch(nBits){
    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:
    	newtio.c_cflag |= CS8;
    	break;
}

1.5 设置校验位

/* 设置校验位 */
switch(nEvent){
    case 'O':                 
        newtio.c_cflag |= PARENB;
        newtio.c_cflag |= PARODD;
        newtio.c_iflag |= (INPCK | ISTRIP);
        break;

    case 'E':                  
        newtio.c_iflag |= (INPCK | ISTRIP);
        newtio.c_cflag |= PARENB;
        newtio.c_cflag &= ~PARODD;
        break;

    case 'N':                   
        newtio.c_cflag &= ~PARENB;
        break;

    case 'S':                  
    	newtio.c_cflag &= ~PARENB;
    	break;

    default:
        newtio.c_cflag &= ~PARENB;
        break;
}

1.6 设置停止位

/* 设置停止位 */
switch (nStop){
    case 1 :
    	newtio.c_cflag &= ~CSTOPB;
    	break;

    case 2 :
    	newtio.c_cflag |= CSTOPB;
    	break;

    default:
    	newtio.c_cflag &= ~CSTOPB;
    	break;
}

1.7 设置超时

下面两句代码就是核心部分:
先来了解一种情况,比如a发送给b 64个字节的数据,正常情况下我们都希望b一次性收完,但是实际情况
不可能是一次收完的,b都会断断续续的收到;
下面这两句代码就是为了防止上面的这种情况发生,希望b会"等一下",等待a发完,但是假如b等了,a又不发了
这样就很难受了,所以如下2个参数就是为了防止这种情况发生;

c_cc[VTIME] = 3;
c_cc[VMIN] = 64;
首先read是会阻塞的;
64的意思是最小要读取到64个字节才返回,否则阻塞在read函数上;
3的意思就是假如在3*100ms内收不到64个字节,就强制返回,解除read阻塞;

其实这两个参数还有其他含义,比如:

newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 64;
代表的意思是:
首先read是会阻塞的;
死等,必须等到64个字节再返回,没有接受超时时间;

newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
代表的意思是:
首先read是不会阻塞的;
收不到就算了,read是不阻塞的;

newtio.c_cc[VTIME] = 4;
newtio.c_cc[VMIN] = 0;



newtio.c_cc[VTIME]	= 3;
newtio.c_cc[VMIN] 	= 64;

1.8 完整代码

#include <stdio.h>  
#include <stdlib.h>
#include <termios.h>
#include <unistd.h> 
#include <sys/types.h>
#include <sys/stat.h> 
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/wait.h>
#include <errno.h>
#include <linux/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <pthread.h>


int g_fd;
#define DEBUG(fmt, arg...) printf(fmt, ##arg)


static speed_t getBaudrate(int baudrate)
{
	switch(baudrate) {
		case 0: 		return B0;
		case 50: 		return B50;
		case 75: 		return B75;
		case 110: 		return B110;
		case 134: 		return B134;
		case 150: 		return B150;
		case 200: 		return B200;
		case 300: 		return B300;
		case 600: 		return B600;
		case 1200: 		return B1200;
		case 1800: 		return B1800;
		case 2400: 		return B2400;
		case 4800: 		return B4800;
		case 9600: 		return B9600;
		case 19200: 	return B19200;
		case 38400: 	return B38400;
		case 57600: 	return B57600;
		case 115200: 	return B115200;
		case 230400: 	return B230400;
		case 460800: 	return B460800;
		case 500000: 	return B500000;
		case 576000: 	return B576000;
		case 921600: 	return B921600;
		case 1000000: 	return B1000000;
		case 1152000: 	return B1152000;
		case 1500000: 	return B1500000;
		case 2000000: 	return B2000000;
		case 2500000: 	return B2500000;
		case 3000000: 	return B3000000;
		case 3500000: 	return B3500000;
		case 4000000: 	return B4000000;
		default: 		return -1;
	}
}

int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
    struct termios newtio,oldtio;   //创建

	
	
	speed_t bSpeed = B115200;

    if(tcgetattr(fd, &oldtio) != 0){ //判断你的驱动节点是不是一个串口设备,假如不反回0,就不是串口设备
        return -1;
    }

    bzero(&newtio, sizeof(newtio));

    // 忽略调制解调器线路状态 | 使用接受器
    newtio.c_cflag |= (CLOCAL | CREAD);  
    /* 清空数据位的值 */
    newtio.c_cflag &= ~CSIZE;

    newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

    /* 关闭处理后输出 */
    newtio.c_oflag &= ~OPOST;

	/* 设置数据位 */
    switch(nBits){
	    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:
	    	newtio.c_cflag |= CS8;
	    	break;
    }
	/* 设置校验位 */
    switch(nEvent){
	    case 'O':                 
	        newtio.c_cflag |= PARENB;
	        newtio.c_cflag |= PARODD;
	        newtio.c_iflag |= (INPCK | ISTRIP);
	        break;

	    case 'E':                  
	        newtio.c_iflag |= (INPCK | ISTRIP);
	        newtio.c_cflag |= PARENB;
	        newtio.c_cflag &= ~PARODD;
	        break;

	    case 'N':                   
	        newtio.c_cflag &= ~PARENB;
	        break;

	    case 'S':                  
	    	newtio.c_cflag &= ~PARENB;
	    	break;

	    default:
	        newtio.c_cflag &= ~PARENB;
	        break;
    }

	bSpeed = getBaudrate(nSpeed);
	cfsetispeed(&newtio, bSpeed);
	cfsetospeed(&newtio, bSpeed);

	/* 设置停止位 */
    switch (nStop){
	    case 1 :
	    	newtio.c_cflag &= ~CSTOPB;
	    	break;

	    case 2 :
	    	newtio.c_cflag |= CSTOPB;
	    	break;

	    default:
	    	newtio.c_cflag &= ~CSTOPB;
	    	break;
    }

	/*
		下面两句代码就是核心部分:
		先来了解一种情况,比如a发送给b 64个字节的数据,正常情况下我们都希望b一次性收完,但是实际情况
		不可能是一次收完的,b都会断断续续的收到;
		下面这两句代码就是为了防止上面的这种情况发生,希望b会"等一下",等待a发完,但是假如b等了,a又不发了
		这样就很难受了,所以如下2个参数就是为了防止这种情况发生;
		
		c_cc[VTIME] = 3;
		c_cc[VMIN] = 64;
		首先read是会阻塞的;
		64的意思是最小要读取到64个字节才返回,否则阻塞在read函数上;
		3的意思就是假如在3*100ms内收不到64个字节,就强制返回,解除read阻塞;

		其实这两个参数还有其他含义,比如:
		newtio.c_cc[VTIME]	= 0;
    	newtio.c_cc[VMIN] 	= 64;
    	代表的意思是: 
    	首先read是会阻塞的;
    	死等,必须等到64个字节再返回,没有接受超时时间;

    	newtio.c_cc[VTIME]	= 0;
    	newtio.c_cc[VMIN] 	= 0;
    	代表的意思是:
    	首先read是不会阻塞的;
    	收不到就算了,read是不阻塞的;

    	newtio.c_cc[VTIME]	= 4;
    	newtio.c_cc[VMIN] 	= 0;
    	
	*/

    newtio.c_cc[VTIME]	= 3;
    newtio.c_cc[VMIN] 	= 64;

    tcflush(fd, TCIOFLUSH); //清空缓冲区
    if((tcsetattr(fd, TCSANOW, &newtio)) != 0){ //把newtio的属性设置到当前的控制台
        return -1;
    }
    
    return 0;
}

int open_port(int fd, char *path)
{

	/*
		O_NOCTTY: 不要把它当成串口控制台,因为它是通信串口,不是调试串口 
		O_NDELAY: 不要阻塞
	*/
	fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY);  
    if (fd < 0)
    {
    	perror("open_oprt failed");
        return(-1);
    }

    DEBUG("fcntl: %d\n", fcntl(fd, F_SETFL, 0));  //再重新设置成非阻塞,保险

    return fd;
}



/*
	path_utf: 串口驱动节点,nuc972的驱动节点是/dev/ttyS*,不是所有的板子都叫ttySAC*,有的叫ttyO*,ttyS*,ttyAMA*等等
	baud: 波特率,比如9600,115200等
	databits:数据位,只有5,6,7,8
	parity_utf: 校验位,只有'O','E','N','S'
	stopbits: 停止位,只有1, 1.5, 2
	
	
	例如:
	char a = 'O';
	OpenSerialPort("/dev/ttySAC2", 115200, 8, &a, 1);

*/
int OpenSerialPort(char *path_utf, int baud, int databits, char *parity_utf, int stopbits)
{
	int fd, result;

	fd = open_port(fd, path_utf);

	result = set_opt(fd, baud, databits, parity_utf[0], stopbits);
	if(result < 0){
		close(fd);
		return result;
	}
	
    return fd;
}


/* 注意,不要在子线程里scanf或者fgets     */
void *read_thread(void *args)
{
	char buf[64];
	
	while(1){
		memset(buf, 0 ,sizeof(buf));
		read(g_fd, buf, sizeof(buf));
		printf("recv=%s\n", buf);
	}
}

int main(int argc, char **argv)
{
	char buf[64];
	char cmd[64]="hahahaha";
	pthread_t tid;

	g_fd = OpenSerialPort(argv[1], 9600, 8, "N", 1);

	pthread_create(&tid, NULL, read_thread, NULL);

	while(1){
		write(g_fd, cmd, strlen(cmd));
		sleep(1);
	}

	return 0;
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值