实现目的
在工作中需要长时间给串口发送数据用来测试稳定性,基于此开发了一个小的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)