欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

Linux 串口编程

程序员文章站 2022-08-13 10:09:58
串行接口串行接口 (Serial Interface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信,从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。一条信息的各位数据被逐位按顺序传送的通讯方式称为串行通讯。...

串行接口

串行接口 (Serial Interface) 是指数据一位一位地顺序传送其特点是通信线路简单只要一对传输线就可以实现双向通信从而大大降低了成本特别适用于远距离通信但传送速度较慢一条信息的各位数据被逐位按顺序传送的通讯方式称为串行通讯

串行通讯的特点是数据位的传送按位顺序进行最少只需一根传输线即可完成成本低但传送速度慢串行通讯的距离可以从几米到几千米根据信息的传送方向串行通讯可以进一步分为单工半双工和全双工三种。

同步串行接口SynchronousSerialInterfaceSSI是一种常用的工业用通信接口。。

异步串行是指UARTUniversal Asynchronous Receiver/Transmitter),通用异步接收/发送UART是一个并行输入成为串行输出的芯片通常集成在主板上UART包含TTL电平的串口RS232电平的串口

串口除了RS232,还有RS422,RS485,今天主要说的是RS232,其实USB(Universal Serial Bus)也是串口的一种,它的协议更加复杂,还有采用D+和D-差分信号,抗干扰能力更强,速率更快。

Linux 串口编程

Linux 串口编程

Linux 串口编程

图片都是之前从网上找到

接下来开始讲代码了,主要分成两个部分,一就是串口配置,二是串口读写

int open_uart(char* port)
{
	int fd = open(port,O_RDWR|O_NOCTTY);
	if (fd < 0){
		printf("Open serial %s fail\n",port);
		return -1;
	}
	else{
		//printf("Open serial %s successful\n",port);
	}
	//set bolck mode
	if (fcntl(fd,F_SETFL,0)<0){
		printf("set %s block failed\n",port);
	}
	if(0 == isatty(STDIN_FILENO)){
		printf("standard input is not a terminal device\n");
	}
	//else{
	//	printf("isatty success\n");
	//}
	return fd;
}

Linux 串口编程

配置串口

Linux 串口编程

int set_uart_config(int fd,uart_cfg_t* s_cfg)    
{    
  int   ret;
  unsigned int i;    
  int   speed_arr[] = { B115200, B9600};
  int   name_arr[] = {115200, 9600};
  struct termios options;    

  ret = tcgetattr( fd,&options);    
  if( ret !=  0){    
    printf("ERROR: Get Serial Failed\n");        
    return ret;     
  }    

  for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++){    
    if(s_cfg->speed == name_arr[i]){                 
      cfsetispeed(&options, speed_arr[i]);
      cfsetospeed(&options, speed_arr[i]);
    }    
  }         

  options.c_cflag |= CLOCAL;
  options.c_cflag |= CREAD;

  switch(s_cfg->flow_ctrl)
  {
    case 0 :    
      options.c_cflag &= ~CRTSCTS;    
      break;       
    case 1 :   
      options.c_cflag |= CRTSCTS;    
      break;    
    case 2 :   
      options.c_cflag |= IXON | IXOFF | IXANY;    
      break; 
    default:
      options.c_cflag |= IXON | IXOFF | IXANY;    
      break;  
  }    

  options.c_cflag &= ~CSIZE;    
  switch (s_cfg->data_bits){      
    case 5 :    
      options.c_cflag |= CS5;    
      break;    
    case 6 :    
      options.c_cflag |= CS6;    
      break;    
    case 7 :        
      options.c_cflag |= CS7;    
      break;    
    case 8:        
      options.c_cflag |= CS8;    
      break;      
    default:       
      options.c_cflag |= CS8;
      break;    
  }    

  switch (s_cfg->parity[0]){      
    case 'n':    
    case 'N': 
      options.c_cflag &= ~PARENB;     
      options.c_iflag &= ~INPCK;        
      break;     
    case 'o':      
    case 'O':        
      options.c_cflag |= (PARODD | PARENB);     
      options.c_iflag |= INPCK;                 
      break;     
    case 'e':     
    case 'E':      
      options.c_cflag |= PARENB;           
      options.c_cflag &= ~PARODD;           
      options.c_iflag |= INPCK;          
      break;    
    case 's':    
    case 'S':     
      options.c_cflag &= ~PARENB;    
      options.c_cflag &= ~CSTOPB;    
      break;     
    default:      
      options.c_cflag &= ~PARENB;    
      options.c_cflag &= ~CSTOPB;    
      break;     
  }     

	switch (s_cfg->stop_bits){
		case 1:
			options.c_cflag &= ~CSTOPB;
			break;
		case 2:
			options.c_cflag |= CSTOPB;
			break;
		default:
			options.c_cflag |= CSTOPB;
			break;
	}    

	options.c_oflag &= ~OPOST;    
	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);    
	options.c_cc[VTIME] = 20;      
	options.c_cc[VMIN] = 255;     
	tcflush(fd,TCIFLUSH);    
	ret=tcsetattr(fd,TCSANOW,&options);    
	if (ret != 0){    
		printf("ERROR: com set error!\n");      
		return ret;     
	}    
	return ret;     
}

下面这些是我在写的时候,从网上查找并记录下来的一些笔记:

tcflag_t 是 unsigned short类型,cc_t 是 unsigned char类型

CRTCTS
使用硬件流控制。在高速(19200bps或更高)传输时,使用软件流控制会使效率降低,这个时候必须使用硬件流控制
~CRTCTS不控制
IXOFF
如果设置,为避免tty设备的输入缓冲区溢出,tty设备可以向终端发送停止符^S和开始符^Q,要求终端停止或重新开始
向计算机发送数据。通过停止符和开始符来控制数据流的方式叫软件流控制,软件流控制方式较少用,我们主要还是用
硬件流控制方式。硬件流控制在c_cflag标志中设置。
IXON
如果设置,接收到^S后会停止向这个tty设备输出,接收到^Q后会恢复输出。
IXANY
如果设置,则接到任何字符都会重新开始输出,而不仅仅是^Q字符。

options.c_cflag |= CS8;         //使用8位数据位
options.c_cflag |= CS7;         //使用7位数据位
options.c_cflag |= CS6;         //使用6位数据位
options.c_cflag |= CS5;         //使用5位数据位

CSIZE 先屏蔽其他标志位

options.c_cflag |= CSTOPB;            //2位停止位
options.c_cflag &amp;= ~CSTOPB;       //1位停止位
PARODD               使用奇校验而不是偶校验
PARENB               校验使能位
INPCK                 奇偶校验使能
1. N(None [没有])
2. O Odd 奇校验

3. E Even 偶校验

4. S Space 校验位固定为0

ISIG 当输入INTR、QUIT、SUSP或DSUSP时,产生相应的信号
ICANON  使用标准输入模式
XCASE 在ICANON和XCASE同时设置的情况下,终端只使用大写。
ECHO  显示输入字符
ECHOE  如果ICANON同时设置,ERASE将删除输入的字符
ECHOK 如果ICANON同时设置,KILL将删除当前行
OPOST 处理后输出
open未设置O_NONBLOCK或O_NDELAY的情况下
VTIME定义要求等待的时间量(取值不能大于cc_t)。
VMIN定义了要求等待的最小字节数。

options.c_cc[VTIME] = X;   //设置从获取到1个字节后开始计时的超时时间
options.c_cc[VMIN] = Y;   ? //设置要求等待的最小字节数
1、X=0,Y!=0。函数read()只有在读取了Y个字节的数据或者收到一个信号的时候才返回;
2、X!=0,Y=0。即使没有数据可以读取,read()函数等待X时间量后返回;
3、X!=0,Y!=0。第一个字节数据到时开始,最先满足收到Y个字节或达超时时间X任意一个条件,read()返回;
4、X=0,Y=0。即使读取不到任何数据,函数read也会立即返回

常用函数介绍

1读取当前参数函数
int tcgetattr(int fd,struct termios *termios_p)
fd
open操作后返回的文件句柄
*termios_p为前面介绍的结构体
初始化开始前调用这个函数.

2
获取当前波特率函数
int speed_t cfgetispeed(const struct termios *termios_p)
int speed_t cfgetospeed(const struct termios *termios_p)
*termios_p
为前面介绍的结构体
成功返回0失败返回-1

3
波特率设置函数
int cfsetispeed(struct termios *termios_p,speed_t speed)
int cfsetospeed(struct termios *termios_p,speed_t speed)
*termios_p
为前面介绍的结构体
speed波特率常用B2400B4800B9600B115200B460800
成功返回0失败返回-1

4
清空buffer数据函数
int tcflush(int fd,int queue_selector)
queue_selector:
有三个常用宏定义
TCIFLUSH:清空正读的数据且不会读出
TCOFLUSH:清空正写入的数据且不会发送到终端
TCIOFLUSH:清空所有正在发生的I/O数据.

成功返回0失败返回-1

5设置串口参数函数
int tcsetattr(int fd,int optional_actions,cons struct termios *termios_p)
optional_actions:
有三个常用宏定义
TCSANOW:不等数据传输完毕立即改变属性
TCSADRAIN:等所有数据传输完毕再改变属性
TCSAFLUSH:清空输入输出缓冲区才改变属性

成功返回0失败返回-1

第二部分:串口读写,在这里我使用的是经tx和rx连接起来,自环

int single_uart_test(uart_cfg_t *s_cfg)
{
	int len = 0,res = 0,len_tmp = 0,flag = 0;	
	char tmp[4096];
	bzero(tmp,sizeof(tmp));		
	int fd = open_uart(uart_map(s_cfg->dev_id));
	init_uart(fd,s_cfg);

	if(signal(SIGTSTP,func_quit) == SIG_ERR){
		printf("signal error exit now!\n");
		return -1;
	}	
	if(signal(SIGINT,func_quit) == SIG_ERR){
		printf("signal error exit now!\n");
		return -1;
	}	
	printf("Welcome to com%d test mode:\n",s_cfg->dev_id);
	
	while(true){
		if (!flag) printf("Com%d says:",s_cfg->dev_id);
		flag++;
		fgets(big_buf,BUFFER_SIZE,stdin);
		len = strlen(big_buf);
		//printf("len:%d\n",len);
		write_uart(fd,big_buf,len,s_cfg->dev_id);
		read_uart(fd,s_cfg->dev_id,len,read_buf);	
				
		if( (0 == strncmp(read_buf,"quit",4)) || (0 == strncmp(read_buf,"exit",4))){
			printf("quit console %d test mode \n",s_cfg->dev_id);
			exit(0);
			}
		if ( 0 != strncmp(read_buf,big_buf,sizeof(read_buf))){
			res++;
		}
		//string -'\0'
		if(len == (BUFFER_SIZE-1)){
			memcpy(tmp+len_tmp,read_buf,len);
			len_tmp += len;
			//printf("tmp:%s\n",tmp);
		}else{
			memcpy(tmp+len_tmp,read_buf,len);
			if(res == 0){
				printf("Com%d recv:%s\n",s_cfg->dev_id,tmp);
				printf("Com%d ------->pass \n",s_cfg->dev_id);
				len_tmp = 0;
				bzero(tmp,sizeof(tmp));
			}else{
				printf("Com%d ------->fail \n",s_cfg->dev_id);
				//flushes both data received but not read, and data written but not transmitted.
				tcflush(fd,TCIOFLUSH);
			}
			flag = 0;
		}
		bzero(read_buf,sizeof(read_buf));
		bzero(big_buf,sizeof(big_buf));			
	}
	close_uart(fd,s_cfg->dev_id);
	return err_no;
}

源码放在资源下载,源码是一个48ports串口服务器的串口测试代码,有删减。

本文地址:https://blog.csdn.net/weixin_38452632/article/details/107972517