最近在玩DJI M100,调用API获取GPS位置时发现高程定位完全是错的(负的几百多米),查了一下文档说高程数据是由气压计得到的,而飞行控制时又需要比较可靠的高度信息,于是乎决定用上我们实验室的搭载Ublox芯片的板子搞事情,在子线程通过串口接收板子的定位结果,在主线程调用,开发环境为Ubuntu16.04/14.04,前者为虚拟机,后者为manifold。

1.     串口编程(只读)

Linux串口编程的步骤很简单,打开串口---串口初始化---读写串口---关闭串口

int open_port(int fd)
{fd = open( "/dev/ttyUSB0", O_RDWR);if (-1 == fd){perror("Can't Open Serial Port ttyUSB0");fd = open( "/dev/ttyUSB1", O_RDWR);}elseprintf("open ttyUSB0 .....\n");if(fcntl(fd, F_SETFL, 0)<0) printf("fcntl failed!\n");elseprintf("fcntl=%d\n",fcntl(fd, F_SETFL,0));if(isatty(STDIN_FILENO)==0)printf("standard input is not a terminal device\n");elseprintf("isatty success!\n");printf("fd-open=%d\n",fd);return fd;
}

主要函数:

Open()

pathname:文件路径名,串口在Linux中被看做是一个文件

oflag:一些文件模式选择,有如下几个参数可以设置

· O_RDONLY只读模式

· O_WRONLY只写模式

· O_RDWR读写模式

上面三种模式在传参时必须传入其中一个,另外还有几个可供选择

· O_NOCTTY如果路径名指向终端设备,不要把这个设备用作控制终端。

· O_NONBLOCK设置为非阻塞模式(nonblocking mode)

· 等等。

Fcntl的使用参考http://www.cnblogs.com/lonelycatcher/archive/2011/12/22/2297349.html

Isatty,若为终端设备则返回1(真),否则返回0(假)。

设置串口,包括波特率,校验方式,停止位,数据位等等。详见代码。

int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{struct termios newtio,oldtio;if ( tcgetattr( fd,&oldtio) != 0) {perror("SetupSerial 1");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;}switch( nEvent ){case 'O':newtio.c_cflag |= PARENB;newtio.c_cflag |= PARODD;newtio.c_iflag |= (INPCK | ISTRIP);break;case 'E':newtio.c_iflag |= (INPCK | ISTRIP);newtio.c_cflag |= PARENB;newtio.c_cflag &= ~PARODD;break;case 'N':newtio.c_cflag &= ~PARENB;break;}switch( nSpeed ){case 2400:cfsetispeed(&newtio, B2400);cfsetospeed(&newtio, B2400);break;case 4800:cfsetispeed(&newtio, B4800);cfsetospeed(&newtio, B4800);break;case 9600:cfsetispeed(&newtio, B9600);cfsetospeed(&newtio, B9600);break;case 115200:cfsetispeed(&newtio, B115200);cfsetospeed(&newtio, B115200);break;default:cfsetispeed(&newtio, B9600);cfsetospeed(&newtio, B9600);break;}if( nStop == 1 )newtio.c_cflag &= ~CSTOPB;else if ( nStop == 2 )newtio.c_cflag |= CSTOPB;newtio.c_cc[VTIME] = 5;newtio.c_cc[VMIN] = 50;tcflush(fd,TCIFLUSH);if((tcsetattr(fd,TCSANOW,&newtio))!=0){perror("com set error");return -1;}printf("set done!\n");return 0;
}

注意:newtio.c_cc[VTIME]= 5;newtio.c_cc[VMIN] = 50;貌似将串口读改成了阻塞模式。

参考http://blog.csdn.net/specialshoot/article/details/50707965

2. 多线程

线程的操作主要步骤是创建线程,互斥锁和信号量来保证线程同步并保护数据。

线程创建函数

int          temp = 100;
pthread_t  thread;//定义线程
temp = pthread_create(&thread,NULL,Serialthread,NULL);if(temp != 0){printf("Create thread failed!");return 0;}

Serialthread为线程函数,线程在此处创建时即执行该函数。然后同时地执行该语句后的主线程部分,若主线程结束,则子线程也会跟着结束。因此子线程中需要一个死循环一直从串口读取GPS数据,同样的主线程中也应该通过循环调用串口读出来的数据。

同时如果在其他的源文件中也需要这个子线程中获取到的数据,只需要在源文件对应的头文件中声明extern(类型) (变量名)即可使用。

为保证数据在串口读取更新和在主函数中调用时不乱套,能够同步安全地进行,因此需要引入互斥锁和信号量。何为互斥锁,简单来说,就是在一个线程中访问公共数据(如这里的GPS位置)之前将数据锁住,使得其他线程不能再访问,访问操作结束之后再解锁是的其他线程可以访问或者操作。

主要函数包括pthread_mutex_init,初始化线程锁;pthread_mutex_lock进入线程锁;pthread_mutex_unlock,解锁。

何为信号量,在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。

主要函数包括sem_init,信号量初始化;sem_post,发送信号量;sem_wait,等待信号量。

这样的话,在子线程和主线程里对数据进行操作前先锁住,操作完再解锁,子线程读取数据之后发送信号量,主线程中对数据进行操作之前先等待信号量。

我这里用了DJI写好的两个类,用起来很方便。

DJI_lock::DJI_lock()
{pthread_mutex_init( &m_lock, NULL );
}DJI_lock::~DJI_lock()
{
}void DJI_lock::enter()
{pthread_mutex_lock( &m_lock );
}void DJI_lock::leave()
{pthread_mutex_unlock( &m_lock );
}DJI_event::DJI_event()
{sem_init( &m_sem, 0, 0 );
}DJI_event::~DJI_event()
{
}int DJI_event::set_event()
{int ret = sem_post( &m_sem );return ret;
}int DJI_event::wait_event()
{int ret = sem_wait( &m_sem );return ret;
}

具体实现(包括DJI的两个类)可见demo(下载地址http://download.csdn.net/download/innocent_sheld/10226352),有问题欢迎讨论。

另外还要提的一个是线程操作的pthread_join函数,用来等待一个线程的结束。描述 :pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。

参数 :thread:线程标识符,即线程ID,标识唯一线程。retval: 用户定义的指针,用来存储被等待线程的返回值。

返回值 : 0代表成功。失败,返回的则是错误号。下面是一个小demo。

thread_para son1 = {'k', 10};
thread_para son2 = {'o', 20};void *sonthread(void *argv)
{thread_para *son;son = (thread_para *)argv;for (int k = 0; k < son->count; k++){fputc(son->character, stderr);usleep(1000);}printf("\n");return (void *)23333;
}int main(int argc, char **argv)
{pthread_t thread1;pthread_create(&thread1, NULL, sonthread, (void *)&son1);pthread_t thread2;pthread_create(&thread2, NULL, sonthread, (void *)&son2);int res1, res2;pthread_join(thread1, (void *)&res1);pthread_join(thread2, (void *)&res2);printf("res1 = %d\n", res1);printf("res2 = %d\n", res2);return 0;
}

运行结果:

这个demo的下载地址http://download.csdn.net/download/innocent_sheld/10226323

在主线程中创建了两个线程,但调用的是同一个线程函数,不同的线程有不同的参数,线程创建时即运行线程函数,因此会依此打印o和k,在主线程中又调用了两次pthread_join函数,因此主线程要等待两个子线程结束才能执行后面的语句。注意:如果这里(void *)&提示错误的话error: invalid conversionfrom ‘void*’ to ‘void**’应该使用gcc编译,不能使用g++。大概是因为C++不会将这里的(void*)&解析为void**。

如果您觉得我的博客对您有所帮助,欢迎对我进行小额资助,以帮助我写出更多博文。:)

linux 多线程串口编程总结相关推荐

  1. CSerialPort多线程串口编程工具详解

    1.前言 既然有了MSComm这种简单粗暴的控件,为什么还需要CSerialPort类?这是因为与前者相比,这个类在程序的发布上不需要加入其他的文件,而且CSerialPort提供给我们的函数都是开放 ...

  2. linux实验串行端口程序设计,Linux下串口编程心得(转)

    最近一段时间,需要完成项目中关于Linux下使用串口的一个部分,现在开帖记录过程点滴. 项目的要求是这样的,Qt应用程序主要完成数据采集和发送功能,一开始在google中海搜关键字"Qt串口 ...

  3. Linux 下串口编程(C++ 程序设计)

    串口通信是最简单的通信方式.即使在USB 非常流行的今天,依然保留了串行通信的方式.网络上已经有大量关于Linux下 C++ 串口编程的文章,但是我依然要写这篇博文.因为网络上的资料不是内容太多,就是 ...

  4. Linux下串口编程

    文章目录 串口 驱动 安装 设备文件 测试代码 编译运行 引用 串口 电平之类的就不说了,串口使用的一般包括rs232全双工,rs422四线全双工,rs485两线半双工,rs485四线全双工几种模式, ...

  5. 推荐《Linux 多线程服务器端编程》

    赖勇浩(http://laiyonghao.com) 最近,有一位朋友因为工作需要,需要从网游的客户端编程转向服务器端编程,找我推荐一本书.我推荐了<Linux 多线程服务器端编程--使用 mu ...

  6. Linux多线程网络编程要义丨epoll与reactor原理

    linux多线程网络编程要义 1. epoll原理剖析 2. 单reactor原理以及应用 3. 多reactor原理以及应用 [Linux服务器系列]Linux多线程网络编程要义丨epoll与rea ...

  7. Qt 多线程串口编程

    一.问题 以前串口编程使用第三方的CnComm.h编程,CnComm作者博客链接,使用起来还蛮好的,不过既然用qt了就想着用qt自带的QSerialPort,移植性更好一些,结果折腾了好几天,主要遇到 ...

  8. Linux下串口编程基础

    串口知识 串行接口 (SerialInterface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用 ...

  9. linux多线程服务器编程 豆瓣,陈硕的Blog

    陈硕(giantchen_AT_gmail_DOT_com) 2012-01-28 我在<Linux 多线程服务端编程:使用 muduo C++ 网络库>第 1.9 节"再论 s ...

最新文章

  1. 百度大脑发挥AI“头雁效应” 王海峰:在AI时代共同推动社会智能化升级
  2. 01 背包问题 --- 待续 - -
  3. 表达式目录树(Expression)
  4. python画苹果标志图片_Mac生成APP图标和启动图的脚本
  5. bash: test1: command not found
  6. php为什么容易解密,PHP代码的加密和解密
  7. SQL Server MYSQL 对外键建立索引的必要性
  8. Maven第7篇:聚合、继承、单继承问题详解
  9. HTML5笔记(一)
  10. 即时通讯系统的消息到达率如何保障
  11. 2021-04-15
  12. nvidia 3d vision kit + opengl + 立体视觉程序开发
  13. [QDialog]qt虚拟键盘的实现以及qdateedit实现打开虚拟键盘
  14. DHCP与DHCP中继
  15. 自签名多级证书亲测可用
  16. criteria使用详解
  17. amd服务器6300系列,AMD新款Opteron 6300服务器CPU终极评测
  18. arduino 驱动_Arduino驱动的My Little Pony捐款箱
  19. Differential-Linear Cryptanalysis from an Algebraic Perspective 论文阅读笔记
  20. 东南大学计算机科学与工程学院就业情况,院系毕业去向 | 东南大学各学院2020届毕业生毕业去向合集...

热门文章

  1. Linux popen函数的使用总结
  2. [源生万物以养人,人创区块以报猿]:开源社区与项目激励机制的思考
  3. 使用Swift编写脚本
  4. vue路由跳转白屏问题
  5. 《你是我的答案》高热首播 郭晓东吴谨言互怼擦出小火苗
  6. 微信小程序运营和推广方法 新手如何快速上手去找新用户
  7. Maven项目打包成Docker镜像并启用
  8. 技术胖前端代码规范秘籍推荐
  9. 彩虹岛水果服务器维护,《彩虹岛水果》攻略 彩虹岛不相信眼泪,只相信产量...
  10. 1667 修复表中的名字