linux串口 demo 解析原创 代码没有完全测试

57 篇文章 0 订阅
/*
********************************************************************************

*******************************************************************************
*/

#include<stdio.h>      /*标准输入输出定义*/    
#include<stdlib.h>     /*标准函数库定义*/    
#include<unistd.h>     /*Unix 标准函数定义*/    
#include<sys/types.h>     
#include<sys/stat.h>       
#include<fcntl.h>      /*文件控制定义*/    
#include<termios.h>    /*PPSIX 终端控制定义*/    
#include<errno.h>      /*错误号定义*/    
#include<string.h>
#include<pthread.h>

#define RET_ERR  -1    
#define RET_OK   0

#define LEN_BUF_RECV_UNPACK 16
#define LEN_BUF_RECV 64


static int g_fd;
static char s_buf_recv[LEN_BUF_RECV_UNPACK];

/*
******************************************************************  
*名称:             uart_open  
*功能:             打开串口并返回串口设备文件描述  
*入口参数:         fd      文件描述符
                    dev    串口号(ttyS0,ttyS1,ttyS2)  
*出口参数:正确返回为0,错误返回为-1  
******************************************************************
*/    
int uart_open(char*dev, int *fd)
{    
	int ret;
	
	*fd = open(dev, O_RDWR|O_NOCTTY|O_NDELAY);  
	printf("fd->open=%d\n", *fd);   
	if (0 > *fd) {    
		printf("Can't Open Serial Port");    
		return RET_ERR;    
	}    
	//恢复串口为阻塞状态
	ret = fcntl(*fd, F_SETFL, 0);  
	printf("fcntl=%d\n", ret);                                  
	if(0 > ret) {    
		printf("fcntl failed!\n");    
		return RET_ERR;    
	}  
	//测试是否为终端设备        
	if(0 == isatty(STDIN_FILENO)) {    
		printf("standard input is not a terminal device\n");    
		return RET_ERR;    
	}     
	printf("isatty success!\n");    
	    
	
	return RET_OK;    
}    



/*
******************************************************************  
*名称:             uart_close  
*功能:             关闭串口并返回串口设备文件描述  
*入口参数:         fd          文件描述符   
                    port        串口号(ttyS0,ttyS1,ttyS2)  
*出口参数:void  
******************************************************************
*/         
void uart_close(int fd)    
{    
    close(fd);    
}    
     
     
     
/*
******************************************************************  
*名称:             uart_set  
*功能:             设置串口数据位,停止位和效验位  
*入口参数:         fd          串口文件描述符
*                   speed       串口速度  
*                   flow_ctrl   数据流控制  
*                   databits    数据位   取值为 7 或者8  
*                   stopbits    停止位   取值为 1 或者2  
*                   parity      效验类型 取值为N,E,O,,S  
*出口参数:正确返回为0,错误返回为-1  
******************************************************************
*/    
int uart_set(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)    
{      
	int i, status;    
	int speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300};    
	int name_arr[] = {115200, 19200, 9600, 4800, 2400, 1200, 300};    
	
	struct termios options;    
	
	/*  tcgetattr(fd,&options)得到与fd指向对象的相关参数,并将它们保存于options,该函数还可以测试配置是否正确,
	该串口是否可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1.  */    
	if(0 != tcgetattr(fd, &options)) {    
		printf("SetupSerial 1\n");        
		return RET_ERR;     
	}    
	
	//设置串口输入波特率和输出波特率    
	for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++) {    
		if  (speed == name_arr[i]) {                 
			cfsetispeed(&options, speed_arr[i]);     
			cfsetospeed(&options, speed_arr[i]);      
		}    
	}         
	
	//修改控制模式,保证程序不会占用串口    
	options.c_cflag |= CLOCAL;    
	//修改控制模式,使得能够从串口中读取输入数据    
	options.c_cflag |= CREAD;    
	
	//设置数据流控制    
	switch(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;    
	}    
	//设置数据位    
	//屏蔽其他标志位    
	options.c_cflag &= ~CSIZE;    
	switch (databits) {      
	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:       
		printf("Unsupported data size\n");    
		return RET_ERR;     
	}    
	//设置校验位    
	switch (parity) {      
	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:      
		printf("Unsupported parity\n");        
		return RET_ERR;     
	}     
	// 设置停止位     
	switch (stopbits) {      
	case 1:       
		options.c_cflag &= ~CSTOPB; 
		break;     
	case 2:       
		options.c_cflag |= CSTOPB; 
		break;    
	default:       
	       printf("Unsupported stop bits\n");     
	       return RET_ERR;    
	}    
	
	//修改输出模式,原始数据输出    
	options.c_oflag &= ~OPOST;    
	
	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);    
	//options.c_lflag &= ~(ISIG | ICANON);    
	options.c_iflag &= ~ICRNL; //禁止将输入的CR转换为NL
	options.c_iflag &= ~(IXON); //清bit位 关闭流控字符
	//设置等待时间和最小接收字符    
	options.c_cc[VTIME] = 0; /* 读取一个字符等待1*(1/10)s */      
	options.c_cc[VMIN] = 0; /* 读取字符的最少个数为1 */    
	
	//如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读    
	tcflush(fd,TCIFLUSH);    
	
	//激活配置 (将修改后的termios数据设置到串口中)    
	if (0 != tcsetattr(fd,TCSANOW,&options)) {    
		printf("com set error!\n");      
		return RET_ERR;     
	}   
	
	return RET_OK;     
}    



/*
******************************************************************  
*名称:                uart_init()  
*功能:                串口初始化  
*入口参数:            fd         文件描述符    
*                      speed      串口速度  
*                      flow_ctrl  数据流控制  
*                      databits   数据位   取值为 7 或者8  
*                      stopbits   停止位   取值为 1 或者2  
*                      parity     效验类型 取值为N,E,O,,S  
*                        
*出口参数:正确返回为0,错误返回为-1   
******************************************************************
*/    
int uart_init(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)    
{    
	int ret;    
	//设置串口数据帧格式    
	//ret = uart_set(fd, 115200, 0, 8, 1, 'N');                                                             
	ret = uart_set(fd, speed, flow_ctrl, databits, stopbits, parity);     
	return  ret;   
        
}    
     
   
   
/*
*******************************************************************  
* 名称:            uart_send  
* 功能:            发送数据  
* 入口参数:        fd           文件描述符      
*                   buf     存放串口发送数据  
*                   data_len     一帧数据的个数  
* 出口参数:        正确返回为0,错误返回为-1  
******************************************************************
*/    
int uart_send(int fd, char *buf, int data_len)    
{    
	int len = 0;    
	
	len = write(fd, buf, data_len);    
	printf("send data is %s\n", buf);
	if (len != data_len) {                     
		tcflush(fd, TCOFLUSH);    
		return RET_ERR;    
	}    
	
	return RET_OK;
}    

/*
******************************************************************  
* 名称:            uart_unpack  
* 功能:            串口数据解包  
* 入口参数:        c         串口数据    
*                   
* 出口参数:        正确返回为0,错误返回为-1    
******************************************************************
*/    
static int uart_unpack(unsigned char c)
{
	//printf("%s is running\r\n",__FUNCTION__);
	static int flg, index;
	
	/*接收到的数据开头为A*/
	if('A' == c) {
		flg = 1;
		index = 0;
	} else if('\n' == c && 0 < index && '\r' == s_buf_recv[index - 1]) { /*以回车换行结束如:A123456\r\n*/		
		//buf[index] = c;
		index = 0;
		flg = 0;
		return RET_OK;
	}
	/*没找到继续找*/
	if (1 == flg) {
		s_buf_recv[index++] = c;		
		if(index >= sizeof(s_buf_recv))
			index = 0;
	}
	
	return RET_ERR;
}
/*
******************************************************************  
* 名称:            uart_recv_data_frame  
* 功能:            串口数据每帧数据解析  
* 入口参数:         
* 出口参数:        正确返回为0,错误返回为-1    
******************************************************************
*/    
static int uart_recv_data_frame()
{
	int len;
	unsigned char type;
	
	printf("buf0-7 %x %x %x %x  %x %x %x %x", s_buf_recv[0], s_buf_recv[1],s_buf_recv[2],s_buf_recv[3],s_buf_recv[4],s_buf_recv[5],s_buf_recv[6],s_buf_recv[7]);
	len = s_buf_recv[1];
	if ('A' != s_buf_recv[0] || 0 >= len || sizeof(s_buf_recv) <= len + 2 || 0x0d != s_buf_recv[len + 2]) {
		printf("unknow uart_recv_data_frame:%d\n", len);
		return RET_ERR;		
	}
	type = (unsigned char)s_buf_recv[2];
	switch(type){
	case 0xAA: 		  			            	
        	printf("uart_recv_data_frame:0xaa\n");	
        	break;    
	default:	
        	printf("unknow uart_recv_data_frame:%x\n", type);		
        	break;			
	}
	
	return RET_OK;
}
/*
******************************************************************  
* 名称:            uart_recv_thread  
* 功能:            接收串口数据线程  
* 入口参数:         
* 出口参数:        错误返回为-1    
******************************************************************
*/  
static void *uart_recv_thread(void *arg)
{
	int ret, i, n;
	fd_set readfds;
	char buf[LEN_BUF_RECV];
	
	while(1) {
		FD_ZERO(&readfds);
		FD_SET(g_fd, &readfds);
		
		
		ret = select(g_fd + 1, &readfds, NULL, NULL, NULL);
		//memset(buf, 0, sizeof(buf));
		
		if(0 >= ret) {
			printf("select failed %d\n", ret); //0超时 1失败
			continue;
		} 
		if(0 >= FD_ISSET(g_fd, &readfds)){
			printf("FD_ISSET failed\n"); 
			continue;
		} 
		do {
			n = read(g_fd, buf, LEN_BUF_RECV);	//配置为非阻塞
			if(0 >= n) {
				printf("read failed %d\n", n); 
				//conitnue;
				break;
			}	
			for (i = 0; i < n; i++) {
				ret = uart_unpack(buf[i]);
				if (RET_OK == ret) {
					uart_recv_data_frame();	
				}
			}
		}while(0 < n);
	}
	
	return NULL;
}

/*
********************************************************************************

*******************************************************************************
 */
 
 
int main(int argc, char **argv)    
{
	int ret, len, i;            
	
	char rcv_buf[256];             
	char send_buf[256];
	pthread_t p_serial;
	
	
	if(argc != 2) {    
		printf("Usage: %s /dev/ttySn 0      #(send data)\n",argv[0]);
		printf("Usage: %s /dev/ttySn 1      #1(receive data)\n",argv[1]);
		printf("open failure : %s\n", strerror(errno));
		
		return RET_ERR;    
	}    
	ret = uart_open(argv[1], &g_fd); //打开串口,返回文件描述符   
	// fd=open("dev/ttyS1", O_RDWR);
	//printf("fd= \n",fd);
	
	ret = uart_init(g_fd, 9600, 0, 8, 1, 'N');    
	printf("Set Port Exactly!\n"); 
	//sleep(1);   
	if(RET_ERR == ret) {
		printf("ret init = %d\n",ret);
		return RET_ERR; 
	}    
	pthread_create(&p_serial, NULL, uart_recv_thread, NULL);/*线程函数在下面*/
	
	
	fgets(send_buf, 256, stdin);   //输入内容,最大不超过40字节,fgets能吸收回车符,这样pc收到的数据就能自动换行     
	for(i = 0; i < 10; i++) {    
		len = uart_send(g_fd, send_buf, 40);    
		if(len > 0)    
			printf(" %d time send %d data successful\n",i,len);    
		else    
			printf("send data failed!\n");    
		              
		sleep(1);    
	}    
	uart_close(g_fd);                 
     
}    

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值