Linux下串口编程(C语言版本)

您所在的位置:网站首页 linux写c语言代码 Linux下串口编程(C语言版本)

Linux下串口编程(C语言版本)

2024-02-06 01:04| 来源: 网络整理| 查看: 265

Linux 系统下串口编程

1.准备工具

案例选择在Ubuntu下创建虚拟串口,作为收发使用,需要用到socat命令。 首先进行安装,本人已经安装好了,使用安装命令后,所以下面会提示一些信息,记得连网^ ^

root@lidimini-virtual-machine:/home/lidimini# apt install socat 正在读取软件包列表... 完成 正在分析软件包的依赖关系树 正在读取状态信息... 完成 socat 已经是最新版 (1.7.3.2-2ubuntu2)。 升级了 0 个软件包,新安装了 0 个软件包,要卸载 0 个软件包,有 119 个软件包未被升级。 root@lidimini-virtual-machine:/home/lidimini#

执行下面这句话

root@lidimini-virtual-machine:/home/lidimini# socat -d -d pty,raw,echo=0 pty,raw,echo=0

然后程序会停留,如下图:

root@lidimini-virtual-machine:/home/lidimini# socat -d -d pty,raw,echo=0 pty,raw,echo=0 2021/04/28 11:50:23 socat[6045] N PTY is /dev/pts/1 2021/04/28 11:50:23 socat[6045] N PTY is /dev/pts/2 2021/04/28 11:50:23 socat[6045] N starting data transfer loop with FDs [5,5] and [7,7]

看到/dev/pts/1 和 /dev/pts/2 就是一组互为收发的串口

2.此案例用到的库

#include #include #include #include #include #include #include #include #include #include #include #include #include

3.操作流程

Linux下设备皆文件,要操作串口设备与操作其他文件一样,需要open,write,read函数

3.1 首先打开设备文件

int serport1fd; serport1fd = open(argv[1],O_RDWR | O_NOCTTY | O_NDELAY);//读写模式,不成为控制终端程序,不受其他程序输出输出影响 //argv[1] 为设备路径 if(serport1fd tcflag_t c_iflag; /* input mode flags */ tcflag_t c_oflag; /* output mode flags */ tcflag_t c_cflag; /* control mode flags */ tcflag_t c_lflag; /* local mode flags */ cc_t c_line; /* line discipline */ cc_t c_cc[NCCS]; /* control characters */ speed_t c_ispeed; /* input speed */ speed_t c_ospeed; /* output speed */ #define _HAVE_STRUCT_TERMIOS_C_ISPEED 1 #define _HAVE_STRUCT_TERMIOS_C_OSPEED 1 }; //这个文件在termios.h中

一般串口通信需要对 c_cflag 、c_cc以及c_ispeed和c_ospeed进行设置,即可满足基本的收发需求,下面将对c_cflag进行说明,这个参数要设置的东西有点多。

/* c_cflag bit meaning */ #ifdef __USE_MISC # define CBAUD 0010017 #endif #define B0 0000000 /* hang up */ #define B50 0000001 #define B75 0000002 #define B110 0000003 #define B134 0000004 #define B150 0000005 #define B200 0000006 #define B300 0000007 #define B600 0000010 #define B1200 0000011 #define B1800 0000012 #define B2400 0000013 #define B4800 0000014 #define B9600 0000015 #define B19200 0000016 #define B38400 0000017 #ifdef __USE_MISC # define EXTA B19200 # define EXTB B38400 #endif #define CSIZE 0000060 /*数据位屏蔽*/ #define CS5 0000000 /*5,6,7,8为数据位*/ #define CS6 0000020 #define CS7 0000040 #define CS8 0000060 #define CSTOPB 0000100 /*停止位*/ #define CREAD 0000200 /*接收标志位*/ #define PARENB 0000400 /*奇偶校验位开启标志位*/ #define PARODD 0001000 /*奇校验,否则偶校验*/ #define HUPCL 0002000 #define CLOCAL 0004000 /*本地连接标志位*/ #ifdef __USE_MISC # define CBAUDEX 0010000 #endif #define B57600 0010001 #define B115200 0010002 #define B230400 0010003 #define B460800 0010004 #define B500000 0010005 #define B576000 0010006 #define B921600 0010007 #define B1000000 0010010 #define B1152000 0010011 #define B1500000 0010012 #define B2000000 0010013 #define B2500000 0010014 #define B3000000 0010015 #define B3500000 0010016 #define B4000000 0010017 #define __MAX_BAUD B4000000 #ifdef __USE_MISC # define CIBAUD 002003600000 /* input baud rate (not used) */ # define CMSPAR 010000000000 /* mark or space (stick) parity */ # define CRTSCTS 020000000000 /* flow control */ #endif

CSTOPB ——停止位 值为2时,为两位停止,值为1时,为1位停止。 CREAD ——接收标志位, 置位后则开启。 PARENB——奇偶校验位开启标志位, 置位则代表开启 PARODD——奇校验,否则偶校验 CLOCAL——本地连接标志位 ,置位则开启连接 CSIZE——数据位屏蔽 ,置位则开启数据位屏蔽(本人没试过开启)

3.4 进行c_cflag设置

typedef struct termios termios_t; termios_t *ter_s = malloc(sizeof(*ter_s)); bzero(ter_s,sizeof(*ter_s));//初始化,清零 ter_s->c_cflag |= CLOCAL | CREAD; //激活本地连接与接受使能 ter_s->c_cflag &= ~CSIZE;//失能数据位屏蔽 ter_s->c_cflag |= CS8;//8位数据位 ter_s->c_cflag &= ~CSTOPB;//1位停止位 ter_s->c_cflag &= ~PARENB;//无校验位

下面设置波特率

cfsetispeed(ter_s,B115200);//设置输入波特率 cfsetospeed(ter_s,B115200);//设置输出波特率

调用上面两个函数进行输出波特率设置,输入波特率设置,其实就是对下面这个两个参数进行设置。

speed_t c_ispeed; /* input speed */ speed_t c_ospeed; /* output speed */

3.5 进行对c_cc进行设置,c_cc是个数组,数组下标对应的元素含义如下:

/* c_cc characters */ #define VINTR 0 #define VQUIT 1 #define VERASE 2 #define VKILL 3 #define VEOF 4 #define VTIME 5 #define VMIN 6 #define VSWTC 7 #define VSTART 8 #define VSTOP 9 #define VSUSP 10 #define VEOL 11 #define VREPRINT 12 #define VDISCARD 13 #define VWERASE 14 #define VLNEXT 15 #define VEOL2 16

具体的含义还没有查,不过案例满足基本的串口收发,需要设置VTIME,VMIN即可。

ter_s->c_cc[VTIME] = 0; ter_s->c_cc[VMIN] = 0; /*1 VMIN> 0 && VTIME> 0 VMIN为最少读取的字符数,当读取到一个字符后,会启动一个定时器,在定时器超时事前,如果已经读取到了VMIN个字符,则read返回VMIN个字符。如果在接收到VMIN个字符之前,定时器已经超时,则read返回已读取到的字符,注意这个定时器会在每次读取到一个字符后重新启用,即重新开始计时,而且是读取到第一个字节后才启用,也就是说超时的情况下,至少读取到一个字节数据。 2 VMIN > 0 && VTIME== 0 在只有读取到VMIN个字符时,read才返回,可能造成read被永久阻塞。 3 VMIN == 0 && VTIME> 0 和第一种情况稍有不同,在接收到一个字节时或者定时器超时时,read返回。如果是超时这种情况,read返回值是0。 4 VMIN == 0 && VTIME== 0 这种情况下read总是立即就返回,即不会被阻塞。----by 解释粘贴自博客园 */

设置好上面参数后调用函数

tcflush(serport1fd,TCIFLUSH);//刷清未处理的输入和或输出 if(tcsetattr(serport1fd,TCSANOW,ter_s) != 0){ printf("com set error!\r\n"); }

tclflush的操作为对串口的文件描述符进行刷新设置,TCIFLUSH意思—— 刷新收到的数据但是不读

4.串口收发

下面的发送函数、接收函数分别用线程来实现。

1.发送函数

typedef struct serial_data{ char databuf[100];//发送/接受数据 int serfd;//串口文件描述符 }ser_Data; void *sersend(void *arg)//串口发送线程函数 { ser_Data *snd = (ser_Data *)arg ; int ret; while(1){ ret = write(snd->serfd,snd->databuf,strlen(snd->databuf)); if(ret > 0){ printf("send success, data is %s\r\n",snd->databuf); }else{ printf("send error!\r\n"); } usleep(300000); /* if(发生中断) break;//退出 */ } }

2.接收函数

void *serrecv(void *arg)//串口发送线程函数 { ser_Data *rec= (ser_Data *)arg ; int ret; while(1){ ret = read(rec->serfd,rec->databuf,1024); if(ret > 0){ printf("recv success,recv size is %d,data is %s\r\n",ret,rec->databuf); }else{ /* 什么也不做 */ } usleep(1000); /* if(发生中断) break;//退出 */ } }

收发就是基本的文件读写,直接操作即可。

下面是详细的代码

#include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct termios termios_t; typedef struct serial_data{ char databuf[100];//发送/接受数据 int serfd;//串口文件描述符 }ser_Data; void *sersend(void *arg); void *serrecv(void *arg); int main(int argc,char *argv[]) { pthread_t pid1,pid2; pthread_attr_t *pthread_arr1,*pthread_arr2; pthread_arr1 = NULL; pthread_arr2 = NULL; int serport1fd; /* 进行串口参数设置 */ termios_t *ter_s = malloc(sizeof(*ter_s)); serport1fd = open(argv[1],O_RDWR | O_NOCTTY | O_NDELAY);//不成为控制终端程序,不受其他程序输出输出影响 if(serport1fd printf("com set error!\r\n"); } char buffer[] = {"hello my world!\r\n"}; char recvbuf[100] = {}; ser_Data snd_data; ser_Data rec_data; snd_data.serfd = serport1fd; rec_data.serfd = serport1fd; memcpy(snd_data.databuf,buffer,strlen(buffer));//拷贝发送数据 pthread_create(&pid1,pthread_arr1,sersend,(void *)&snd_data); pthread_create(&pid2,pthread_arr2,serrecv,(void *)&rec_data); ssize_t sizec; while(1){ usleep(100000); } pthread_join(pid1,NULL); pthread_join(pid2,NULL); free(ter_s); return 0; } void *sersend(void *arg)//串口发送线程函数 { ser_Data *snd = (ser_Data *)arg ; int ret; while(1){ ret = write(snd->serfd,snd->databuf,strlen(snd->databuf)); if(ret > 0){ printf("send success, data is %s\r\n",snd->databuf); }else{ printf("send error!\r\n"); } usleep(300000); /* if(发生中断) break;//退出 */ } } void *serrecv(void *arg)//串口发送线程函数 { ser_Data *rec= (ser_Data *)arg ; int ret; while(1){ ret = read(rec->serfd,rec->databuf,1024); if(ret > 0){ printf("recv success,recv size is %d,data is %s\r\n",ret,rec->databuf); }else{ /* 什么也不做 */ } usleep(1000); /* if(发生中断) break;//退出 */ } }

测试结果如下: 发送测试:

root@lidimini-virtual-machine:/home/lidimini/桌面/tty# gcc -o tty tty.c -lpthread root@lidimini-virtual-machine:/home/lidimini/桌面/tty# ./tty /dev/pts/1 send success, data is hello my world! send success, data is hello my world! send success, data is hello my world! send success, data is hello my world! send success, data is hello my world! send success, data is hello my world! send success, data is hello my world! ^C root@lidimini-virtual-machine:/home/lidimini# cat /dev/pts/2 hello my world! hello my world! hello my world! hello my world! hello my world! hello my world! hello my world!

接收测试:

recv success,recv size is 16,data is I recv message send success, data is hello my world! recv success,recv size is 16,data is I recv message send success, data is hello my world! recv success,recv size is 16,data is I recv message send success, data is hello my world! recv success,recv size is 16,data is I recv message root@lidimini-virtual-machine:/home/lidimini# echo "I recv message " > /dev/pts/2 root@lidimini-virtual-machine:/home/lidimini# echo "I recv message " > /dev/pts/2 root@lidimini-virtual-machine:/home/lidimini# echo "I recv message " > /dev/pts/2 root@lidimini-virtual-machine:/home/lidimini# echo "I recv message " > /dev/pts/2 root@lidimini-virtual-machine:/home/lidimini# echo "I recv message " > /dev/pts/2

写的有些啰嗦,主要是希望多写一些,怕自己忘了,也希望能提供大家一些帮助吧,写的不好,请多担待,有问题请指出,谢谢。

再次感谢每一位前辈付出!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3