最近在实习的公司做一个ROS系统,有个部分需要控制电机,以及分析电机码盘给上来的数据,用的是串口通信。 由于码盘给上来的数据时间是不固定的,下放命令的时间也是不固定的,所以只能做成异步通信的形式。所以就需要用到多线程编程了。 编程语言是C,但由于另一个部分是企业提供的,C++形式,用到了很多类,所以这里编译用的是g++
这篇博客里写的只是个demo,实现了线程的功能,加上串口通信的在 http://blog.csdn.net/u013793399/article/details/52315657
下面这个代码本来是另一个实习生在网上找来的,但是他说跑起来有乱码的现象。我把代码拿过来之后连编译都过不去,后来搞了好几个小时才搞通,顺便找到了乱码的原因,也是有点坑在里面的,所以特地记录下来。
太长不看的同学可以直接copy下面的代码,亲测能跑
//main.cpp #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <errno.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <termios.h> #include <pthread.h> #include <sys/time.h> #define MAX 2 pthread_t thread[2]; pthread_mutex_t mut; int fd; int set_port(int fd,int nbits) { struct termios newtio,oldtio; if(tcgetattr(fd,&oldtio)!=0) { perror("pei zhi cuo wu1\n"); return -1; } bzero(&newtio,sizeof(newtio)); //清零 newtio.c_cflag |=CLOCAL|CREAD;//用于本地连接和接收使能 newtio.c_cflag &=~CSIZE;//设置数据位 switch(nbits) { case 7: newtio.c_cflag |=CS7;break; case 8: newtio.c_cflag |=CS8;break; } //设置奇校验位 newtio.c_cflag |=PARENB; //设置波特率 cfsetispeed(&newtio,B115200); cfsetospeed(&newtio,B115200); //设置停止位 newtio.c_cflag &=~PARENB; if((tcsetattr(fd,TCSANOW,&newtio))!=0) { perror("pei zhi cuo wu2\n"); return -1; } printf("mistakes over \n"); return 0; } void *thread1(void*) { int i; printf ("thread1 \n"); for( i=0;i<MAX;i++){ pthread_mutex_lock(&mut); if(i==0){ printf("writing times: %d\n",i+1); char buf1[]="12\r\n\0"; int length=4;//sizeof(buf1); //5-1=4, '\0' shall not be transmitted int j=write(fd,buf1,length); printf("Writen message: "); puts(buf1); printf("\n"); if(j<0)printf("fa song shi bai\n"); printf("Writen length: %d \n",j); } else if(i==1){ printf("writing times: %d\n",i+1); char buf2[]="Jacksen2\r\n\0"; int length=10;//sizeof(buf2); int j=write(fd,buf2,length); printf("Writen message: "); puts(buf2); printf("\n"); if(j<0)printf("fa song shi bai\n"); printf("Writen length: %d \n",j); } sleep(1); pthread_mutex_unlock(&mut); } printf("thread1 stop\n"); pthread_exit(NULL); } void *thread2(void*) { int j; sleep(1); printf("thread2\n"); char buf[100] = {'\0'}; for (j = 0; j< MAX; j++) { pthread_mutex_lock(&mut); sleep(1); printf("read %d\n",j+1); int k=read(fd,buf,100); printf("k+%d\n",k); puts(buf); pthread_mutex_unlock(&mut); sleep(1); } printf("thread2 Stop\n"); pthread_exit(NULL); } void thread_create(void) { int temp; memset(thread, 0, 2*sizeof(thread)); //comment1 /*创建线程*/ if((temp = pthread_create(&thread[0], NULL, thread1, NULL)) != 0) //comment2 printf("xian chegn 1 faile\n"); else printf("X1send surcess\n"); if((temp = pthread_create(&thread[1], NULL, thread2, NULL)) != 0) //comment3 printf("2 faile\n"); else printf("X2recend surcess\n"); } void thread_wait(void) { /*等待线程结束*/ if(thread[0] !=0) { //comment4 pthread_join(thread[0],NULL); printf("thread1 stop \n"); } if(thread[1] !=0) { //comment5 pthread_join(thread[1],NULL); printf("thread2 stop \n"); } } int main(void) { int i,j,k; fd=open("/dev/ttyUSB0",O_RDWR|O_NOCTTY|O_NDELAY); if(-1==fd)printf("it can't open the communitation\n"); else { i=set_port(fd, 8); if(i<0) { perror("pei zhi cuo wu3\n"); return 0; } pthread_mutex_init(&mut,NULL); printf("creat preadth\n"); thread_create(); printf("Program On..... \n"); while(1); thread_wait(); close(fd); } return 0; }终端编译命令
$ g++ main.cpp -o thread_test -lpthread运行命令
./thread_test先看下全局变量
pthread_t thread[2]; 线程池,这里一共需要读写两个线程。pthread_mutex_t mut; 互斥锁,用于保证每个线程执行时不受打扰。int fd; 要打开的串口。然后看 main 函数
首先打开串口并做错误处理;串口打开成功后,调用 set_port 函数,设置串口通信属性;用内核函数 pthread_mutex_init 初始化线程的 mutex ;自己写的函数 thread_create 创建线程;然后就是死循环,无尽的等待。set_port 函数没什么好说的,跟线程没关系,不做解释了。
pthread_mutex_init 的用法也很固定,没有坑。
thread_create 是自己写的函数
memset初始化线程池,注意sizeof(thread)返回的是指针的大小,可以分配x个线程,就有x个指针,就需要初始化x*sizeof(thread),这里x=2创建两个线程,调用内核函数 pthread_create ,注意第1和第3个参数的写法,第一个参数是线程池里分配给该线程的地址,第三个参数是线程函数,原型是void* (* start_routine)(void*),需要注意。 void thread_create(void) { int temp; memset(thread, 0, 2*sizeof(thread)); //comment1 /*创建线程*/ if((temp = pthread_create(&thread[0], NULL, thread1, NULL)) != 0) //comment2 printf("xian chegn 1 faile\n"); else printf("X1send surcess\n"); if((temp = pthread_create(&thread[1], NULL, thread2, NULL)) != 0) //comment3 printf("2 faile\n"); else printf("X2recend surcess\n"); }最重要的是线程函数的函数头
必须写成 void * threadname (void*)参数列表里的 void* 不能省略,否则会报 类型不能转换 错误 void *thread1(void*) { //something to do }编译的时候也有个大坑
$ g++ main.cpp -o thread_test -lpthread-lpthread必须要加上,否则会报 函数未定义 错误。 原因是,pthread_create函数不在Linux默认的库,编译的时候需要链接其所在的静态库,也就是加上这句话。
其他的一些坑:
乱码出现的原因,是之前上传和接收数据的时候,用了puts函数,打印到屏幕做调试用,但是puts函数的参数只是一个char*,没有长度,那它要打印多少呢?答案就是,打印到出现 ‘\0’为止。乱码解决方法:往下写数据的时候,在结尾加上’\0’,但是write函数的长度参数不变,’\0’也就不会被写进串口,但是调试的时候输出到屏幕就能及时卡住puts不让它乱码; 往上收数据的时候,本来就要先分配一个buffer嘛,初始化这个buffer,让它全部为’\0’,那无论接收多少数据,有效数据之后的那一位,肯定是’\0’