1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > Linux下串口编程(C语言版本)

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

时间:2019-12-25 10:37:07

相关推荐

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

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/04/28 11:50:23 socat[6045] N PTY is /dev/pts/1/04/28 11:50:23 socat[6045] N PTY is /dev/pts/2/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 <stdio.h>#include <stdlib.h>#include <strings.h>#include <string.h>#include <pthread.h>#include <unistd.h>#include <fcntl.h>#include <stdlib.h>#include <error.h>#include <termios.h>#include <malloc.h>#include <sys/types.h>#include <sys/stat.h>

3.操作流程

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

3.1 首先打开设备文件

int serport1fd;serport1fd = open(argv[1],O_RDWR | O_NOCTTY | O_NDELAY);//读写模式,不成为控制终端程序,不受其他程序输出输出影响//argv[1] 为设备路径if(serport1fd < 0){printf("%s open faild\r\n",argv[1]);return -1;//若打开错误则退出返回}

打开之后可进行读写,但是串口设备不同,要设置串口的基本参数,下面需要用到struct termios这个结构体。

3.2 termios这个结构主要包含的成员有

struct termios{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 CBAUD0010017#endif#define B00000000/* hang up */#define B500000001#define B750000002#define B1100000003#define B1340000004#define B1500000005#define B2000000006#define B3000000007#define B6000000010#define B12000000011#define B18000000012#define B24000000013#define B48000000014#define B96000000015#define B192000000016#define B384000000017#ifdef __USE_MISC# define EXTA B19200# define EXTB B38400#endif#define CSIZE0000060 /*数据位屏蔽*/#define CS50000000 /*5,6,7,8为数据位*/#define CS60000020#define CS70000040#define CS80000060 #define CSTOPB0000100 /*停止位*/#define CREAD0000200 /*接收标志位*/#define PARENB0000400 /*奇偶校验位开启标志位*/#define PARODD0001000 /*奇校验,否则偶校验*/#define HUPCL0002000#define CLOCAL0004000 /*本地连接标志位*/#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 00600000/* 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> 0VMIN为最少读取的字符数,当读取到一个字符后,会启动一个定时器,在定时器超时事前,如果已经读取到了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 <stdio.h>#include <stdlib.h>#include <strings.h>#include <string.h>#include <pthread.h>#include <unistd.h>#include <fcntl.h>#include <stdlib.h>#include <error.h>#include <termios.h>#include <malloc.h>#include <sys/types.h>#include <sys/stat.h>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 < 0){printf("%s open faild\r\n",argv[1]);return -1;}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;//无校验位ter_s->c_cc[VTIME] = 0;ter_s->c_cc[VMIN] = 0;/*1 VMIN> 0 && VTIME> 0VMIN为最少读取的字符数,当读取到一个字符后,会启动一个定时器,在定时器超时事前,如果已经读取到了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 解释粘贴自博客园*/cfsetispeed(ter_s,B115200);//设置输入波特率cfsetospeed(ter_s,B115200);//设置输出波特率tcflush(serport1fd,TCIFLUSH);//刷清未处理的输入和/或输出if(tcsetattr(serport1fd,TCSANOW,ter_s) != 0){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 -lpthreadroot@lidimini-virtual-machine:/home/lidimini/桌面/tty# ./tty /dev/pts/1send 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/2hello 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/2root@lidimini-virtual-machine:/home/lidimini# echo "I recv message " > /dev/pts/2root@lidimini-virtual-machine:/home/lidimini# echo "I recv message " > /dev/pts/2root@lidimini-virtual-machine:/home/lidimini# echo "I recv message " > /dev/pts/2root@lidimini-virtual-machine:/home/lidimini# echo "I recv message " > /dev/pts/2

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

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

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。