Linux uart应用程序

实现目的

在工作中需要长时间给串口发送数据用来测试稳定性,基于此开发了一个小的demo,话不多说直接上代码。

源码

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

#define DEF_BAUD	115200
#define SPEED_CNT	5
#define BUF_SIZE	100

/*用来接收轨道数据*/
char buffer[BUF_SIZE];

int speed_arr[SPEED_CNT] = {
	B9600, B19200, B38400, 
	B57600,	B115200
};

int name_arr[SPEED_CNT] = {
	9600, 19200, 38400,
	57600, 115200
};

time_t timeStamp()
{
	time_t time_now = time(NULL);
	return time_now;
}

/**
*@brief  设置串口通信速率
*@param  fd     类型 int  打开串口的文件句柄
*@param  speed  类型 int  串口速度
*@return  0 success or -1 err
*/
int set_speed(int fd, int speed)
{
	int i;
	int status;
	struct termios opt;
	
	tcgetattr(fd, &opt);

	for (i= 0; i<SPEED_CNT; i++)
	{
		if (speed == name_arr[i])
		{
			tcflush(fd, TCIOFLUSH);
			/*  设置串口的波特率 */
			cfsetispeed(&opt, speed_arr[i]);
			cfsetospeed(&opt, speed_arr[i]);
			status = tcsetattr(fd, TCSANOW, &opt);

			if (status != 0)
			{
				perror("tcsetattr set_speed");
				return -1;
			}
			
			return 0;
     	}
		/*清空所有正在发生的IO数据*/
		tcflush(fd, TCIOFLUSH);
   }
   
	printf("Cannot find suitable speed\n");
	return -1;
}

/**
*@brief   设置串口数据位,停止位和效验位
*@param  fd     类型  int  打开的串口文件句柄*
*@param  databits 类型  int 数据位   取值 为 7 或者8*
*@param  stopbits 类型  int 停止位   取值为 1 或者2*
*@param  parity  类型  int  效验类型 取值为N,E,O,,S
*@return  0 success or -1 err
*/
int set_parity(int fd, int databits, int stopbits, int parity)
{

	struct termios options;
	if (tcgetattr(fd, &options) != 0)
	{
		perror("tcgetattr");
		return -1;
	}

	options.c_cflag &= ~CSIZE;
	switch (databits) /*设置数据位数*/
	{
		case 7:
			options.c_cflag |= CS7;
			break;
		case 8:
			options.c_cflag |= CS8;
			break;
		default:
			fprintf(stderr, "Unsupported data size\n");

			return -1;
	}
	
	switch (parity)
  	{
		case 'n':
		case 'N':
			options.c_cflag &= ~PARENB;   /* Clear parity enable */
			options.c_iflag &= ~INPCK;     /* Enable parity checking */
			options.c_iflag &= ~(ICRNL|IGNCR);
			options.c_lflag &= ~(ICANON );
			break;
		case 'o':
		case 'O':
			options.c_cflag |= (PARODD | PARENB);  /* 设置为奇效验*/ 
			options.c_iflag |= INPCK;             /* Disnable parity checking */
			break;
		case 'e':
		case 'E':
			options.c_cflag |= PARENB;     /* Enable parity */
			options.c_cflag &= ~PARODD;   /* 转换为偶效验*/  
			options.c_iflag |= INPCK;       /* Disnable parity checking */
			break;
		case 'S':
		case 's':  /*as no parity*/
			options.c_cflag &= ~PARENB;
			options.c_cflag &= ~CSTOPB;
			break;
		default:
			fprintf(stderr, "Unsupported parity\n");
			return -1;
	}

	/* 设置停止位*/   
	switch (stopbits)
  	{
		case 1:
			options.c_cflag &= ~CSTOPB;
			break;
		case 2:
			options.c_cflag |= CSTOPB;
			break;
		default:
			fprintf(stderr,"Unsupported stop bits\n");
			return -1;
	}

	/* Set input parity option */
	if ((parity != 'n') || (parity != 'N'))
  		options.c_iflag |= INPCK;

	/* 若以O_NONBLOCK 方式open,这两个设置没有作用,等同于都为0 */
	/* 若非O_NONBLOCK 方式open,具体作用可参考其他博客,关键词linux VTIME */
    options.c_cc[VTIME] = 10; // 1s
    options.c_cc[VMIN] = 0; 

	/* 清空正读的数据,且不会读出 */
	tcflush(fd,TCIFLUSH); 
	
	/*采用原始模式通讯*/
	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
	options.c_oflag &= ~OPOST;
	

	/* Update the options and do it NOW */
	if (tcsetattr(fd, TCSANOW, &options) != 0)
  	{
  		perror("SetupSerial 0");
		return -1;
	}

	return 0;
}

int main(int argc, char **argv)
{
	int fd;
	int ret = -1;
	const char *dev = NULL;
	const char *test_string = NULL;
	char *p_buffer = NULL;
	int nread = 0;
	int nwrite = 0;
	
	/* 1、检测传参 */
	if (argc < 2 || argc > 3 ) {
		printf("Usage:%s dev_name [self_loop_test_string]\n", argv[0]);
		exit(1);
	}
	dev = argv[1];
	if (NULL != argv[2]) {
		if (strlen(argv[2]) <= BUF_SIZE) {
			test_string = argv[2];
		} else {
			printf("self_loop_test_string must be smaller than %d.\n", BUF_SIZE);
			return 0;
		}
	}
	
	/* 2、打开串口 */
	fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK);
	if (-1 == fd) {
		printf("Cannot open %s:%s\n", dev, strerror(errno));
		exit(1);
	}
	printf("==== open %s success ====\n", dev);
	
#if 1
	/* 3、初始化设备 */
	if (-1 == set_speed(fd, DEF_BAUD)) {
		printf("Cannot set baudrate to 115200\n");
		close(fd);
		exit(1);
	}
	
  	if (-1 == set_parity(fd, 8, 1, 'N')) {
    	printf("Set Parity Error\n");
		close(fd);
    	exit(1);
  	}
#endif

	if (NULL != test_string) {
		/*开始自发自收测试*/
		/* 写串口 */
		do {
			nwrite = write(fd, test_string, strlen(test_string));
		} while(nwrite <= 0);
		printf("send %d bytes data.\n", nwrite);

		/* 清空buffer */
		memset(buffer, 0, BUF_SIZE);
		p_buffer = buffer;
		nread = 0;

		/* 读串口 */
		do {
			ret = read(fd, p_buffer, 64);

			if (ret > 0) {
				printf("read %d bytes data:%s\n", ret, p_buffer);
				p_buffer += ret;
				nread += ret;
			}
		} while(nread < nwrite);
		printf("all read %d bytes data:%s\n", nread, buffer);
		printf("====      test %s      ====\n", (0 == strcmp(buffer, test_string)) ? "success." : "error!!!");

		close(fd);
		return 0;
	}

	/*开始测试*/
	/*循环读取并回写串口内容*/
	printf("start read and pass back\n");
	while(1) {
		/* 清空buffer */
		memset(buffer, 0, BUF_SIZE);
		p_buffer = buffer;
		time_t ts = timeStamp();
		/* 读串口 */
		nread = read(fd, buffer, 64);
		if(nread > 0) {
			printf("read %d bytes,data:%s,time:%ld.\n", nread,p_buffer,ts);
			do {
				ret = write(fd, buffer, nread);
			} while(ret <= 0);

			printf("write %d bytes,data:%s,time:%ld.\n", ret,p_buffer,ts);
		}
		sleep(1);
	}
	
	close(fd);
	
	return 0;
}

测试

第一步:将uart测试程序放到开发板上,执行chmod +x uart,赋予测试程序可执行权限。

第二步:关闭串口,使用网络登录,在telnet中执行: ./uart /dev/ttyS0

第三步:在PC端打开串口调式工具,打开对应串口,自动发送数据

测试效果如下所示:
在这里插入图片描述

改进版

代码

支持测试波特率、数据位(5、6、7、8)、停止位(1、2)、校验位(None、odd、Even、Mark、Space)不同参数

#include <stdio.h>      /*标准输入输出定义*/
#include <stdlib.h>     /*标准函数库定义*/
#include <fcntl.h>      /*文件控制定义*/
#include <termios.h>    /*PPSIX终端控制定义*/
#include <unistd.h>     /*Unix标准函数定义*/
#include <string.h>
#include <sys/time.h>
#include <stdint.h>
#include <sys/select.h>

#define BUF_SIZE	100

char buffer[BUF_SIZE];

time_t timeStamp()
{
	time_t time_now = time(NULL);
	return time_now;
}


int set_parity(int fd, int baudrate,int databits, int stopbits, char parity)
{
   struct termios options;

     // 获取串口参数
    if (tcgetattr(fd, &options) != 0) {
        perror("tcgetattr error");
        return -1;
    }
	
    /*设置输入输出波特率,两者保持一致*/
    switch(baudrate)
    {
      
        case 9600:
            cfsetispeed(&options,B9600);
            cfsetospeed(&options,B9600);
            break;
        case 19200:
            cfsetispeed(&options,B19200);
            cfsetospeed(&options,B19200);
            break;
        case 38400:
            cfsetispeed(&options,B38400);
            cfsetospeed(&options,B38400);
            break;
        case 57600:
            cfsetispeed(&options,B57600);
            cfsetospeed(&options,B57600);
            break;
        case 115200: 
            cfsetispeed(&options,B115200);
            cfsetospeed(&options,B115200);
            break;
        default:
            fprintf(stderr,"Unkown baude!\n");
            return -1;
    }


	switch (databits) /*设置数据位数*/
	{
		case 5:
            options.c_cflag &= ~CSIZE;
            options.c_cflag |= CS5;
            break;
        case 6:
            options.c_cflag &= ~CSIZE;
            options.c_cflag |= CS6;
            break;
        case 7:
            options.c_cflag &= ~CSIZE;
			options.c_cflag |= CS7;
			break;
		case 8:
            options.c_cflag &= ~CSIZE;
			options.c_cflag |= CS8;
			break;
		default:
			fprintf(stderr, "Unsupported data size\n");
    	    return -1;
	}
	
	switch (parity)/*设置校验位*/
  	{
		/*无奇偶校验位*/
        case 'n':
		case 'N':
			options.c_cflag &= ~PARENB;   /* Clear parity enable */
			options.c_iflag &= ~INPCK;     /* Enable parity checking */
			options.c_iflag &= ~(ICRNL|IGNCR);
			options.c_lflag &= ~(ICANON );
			break;
        /*设置奇校验*/
		case 'o':
		case 'O':
			options.c_cflag |= (PARODD | PARENB);  /* 设置为奇效验*/ 
			options.c_iflag |= INPCK;             /* Disnable parity checking */
			break;
        /*设置偶校验*/
		case 'e':
		case 'E':
			options.c_cflag |= PARENB;     /* Enable parity */
			options.c_cflag &= ~PARODD;   /* 转换为偶效验*/  
			options.c_iflag |= INPCK;       /* Disnable parity checking */
			break;
        /*设为空格,即停止位为两位*/
		case 'S':
		case 's':  /*as no parity*/
			options.c_cflag &= ~PARENB;
			options.c_cflag &= ~CSTOPB;
			break;
		default:
			fprintf(stderr, "Unsupported parity\n");
			return -1;
	}

	/* 设置停止位*/   
	switch (stopbits)
  	{
		case 1:
			options.c_cflag &= ~CSTOPB;
			break;
		case 2:
			options.c_cflag |= CSTOPB;
			break;
		default:
			fprintf(stderr,"Unsupported stop bits\n");
			return -1;
	}

	/* Set input parity option */
	if ((parity != 'n') || (parity != 'N'))
  		options.c_iflag |= INPCK;

	/* 若以O_NONBLOCK 方式open,这两个设置没有作用,等同于都为0 */
	/* 若非O_NONBLOCK 方式open,具体作用可参考其他博客,关键词linux VTIME */
    options.c_cc[VTIME] = 10; // 1s
    options.c_cc[VMIN] = 0; 

	/* 清空正读的数据,且不会读出 */
	tcflush(fd,TCIFLUSH); 
	
	/*设置本地为原始模式通讯*/
	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
	options.c_oflag &= ~OPOST;
	

	/* Update the options and do it NOW */
	if (tcsetattr(fd, TCSANOW, &options) != 0)
  	{
  		perror("tcsetattr failed");
		return -1;
	}

	return 0;
}

int main(int argc, char *argv[]) {
    int fd;
    int opt;
    int baudrate,databits,stopbits,flow;
    char parity;
    char *devname = NULL;
    char *p_buffer = NULL;
    int nread = 0;
	int nwrite = 0;
    int ret = -1;
    fd_set read_fds, write_fds;  // 读写文件描述符集合
    int max_fd;                  // 最大文件描述符
   

    //检测传参
   if (argc < 2 || argc > 12 ) {
		printf("Usage:%s  dev_name [-b baudrate] [-d databits] [-s stopbits] [-p parity] \n", argv[0]);
		exit(1);
	} 

     devname = argv[1];
     /* printf("dev_name is : %s\n",argv[1]);
     printf("argc count is %d\n",argc); */

     while ((opt = getopt(argc, argv, "b:d:s:p:")) != -1) {
                switch (opt) {
                case 'b':
                    baudrate =  atoi(optarg); 
                break;
                case 'd':
                    databits  = atoi(optarg);   
                break;
                case 's':
                    stopbits  = atoi(optarg);
                break;
                case 'p':
                    strcpy(&parity, optarg);
                break;
                default: 
                        printf("Usage: %s  dev_name [-b baudrate] [-d databits] [-s stopbits] [-p parity]\n", argv[0]);
                        return -1;
                }
        }
    printf("baudrate is :%d,databits is :%d,stopbits is :%d,parity is :%c\n",baudrate,databits,stopbits,parity);

    // 打开串口设备
    fd = open(devname, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd < 0) {
        perror("open device error");
        return -1;
    }
    printf("==== open %s success ====\n", devname);

#if 1
	/* 初始化设备 */
  	if (-1 == set_parity(fd,baudrate,databits,stopbits,parity)) {
    	printf("Set Parity Error\n");
		close(fd);
    	exit(1);
  	}
#endif
    
    // 初始化文件描述符集合
    FD_ZERO(&read_fds);
    FD_ZERO(&write_fds);
    FD_SET(fd, &read_fds);
    max_fd = fd;

   /*开始测试*/
	/*循环读取并回写串口内容*/
	printf("start read and pass back\n");
	while(1) {

         // 使用select监控串口设备描述符
        int ret = select(max_fd + 1, &read_fds, &write_fds, NULL, NULL);
        if (ret < 0) {
            perror("select error");
            exit(1);
        }
        
        // 如果串口设备可读,则读取数据并打印
        if (FD_ISSET(fd, &read_fds)) {
            /* 清空buffer */
		    memset(buffer, 0, BUF_SIZE);
            p_buffer = buffer;
		    time_t ts = timeStamp();
		    /* 读串口 */
		    nread = read(fd, buffer, 64);
		    if(nread > 0) {
			printf("read %d bytes,data:%s,time:%ld.\n", nread,p_buffer,ts);
			do {
				ret = write(fd, buffer, nread);
			} while(ret <= 0);

			printf("write %d bytes,data:%s,time:%ld.\n", ret,p_buffer,ts);
		    }
            
        }
		
		
	}

    // 关闭串口设备
    close(fd);

    return 0;
}
用法:
Usage:./uart2  dev_name [-b baudrate] [-d databits] [-s stopbits] [-p parity]

exmple: ./uart2 /dev/ttyS1 -b 115200 -d 8 -s 1 -p n

参数说明:
    dev_name: 要测试的设备节点,这里以k510 uart1为例,设备节点为/dev/ttyS1 

    -b baudrate: 波特率,参数支持9600、19200、38400、57600、115200

    -d databits: 数据位数,参数支持5、6、7、8

    -s stopbits: 停止位,参数支持1、2

    -p parity : 校验位,参数支持 n/N(None)、o/O(Odd)、e/E(Even)、s/S(space)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值