Linux下的串口编程及非阻塞模式
本篇介绍了如何在linux系统下向串口发送数据。包括read的阻塞和非阻塞。以及select方法。
打开串口
在Linux系统下,打开串口是通过使用标准的文件打开函数操作的。
#include <fcntl.h>
/* 以读写的方式打开 */
int fd = open( "/dev/ttyUSB0",O_RDWR);
设置串口
所有对串口的操作都是通过结构体 struct termios 和 几个函数实现的。
tcgetattr //获取属性
tcsetattr //设置属性
cfgetispeed //得到输入速度
cfsetispeed //设置输入速度
cfgetospeed //得到输出速度
cfsetospedd //设置输出速度
tcdrain //等待所有输出都被传输
tcflow //挂起传输或接收
tcflush //刷清未决输入和输出
tcsendbreak //送break字符
tcgetpgrp //得到前台进程组ID
tcsetpgrp //设置前台进程组ID
tcgetattr( 0,&oldstdio); //获取默认的配置选项 存储到oldstdio结构体中
tcgetattr( fd,&oldstdio); //获取当前配置选项 存储到oldstdio结构体中
tcsetattr( fd,TCSANOW,&oldstdio); //TCSANOW 修改立即生效
cfgetispeed( &oldstdio); //得到波特率
cfsetispeed(&oldstdio, B115200 ) //设置波特率为115200
即可使用read或open来操作串口的发送与接收。 测试代码:
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h> int serial_send( int fd, char *Data ); int main()
{
int fd;
int num;
struct termios oldstdio; fd = open("/dev/ttyUSB0", O_RDWR );
if( -==fd )
{
printf("cannot open /dev/ttyUSB0\r\n");
return -;
}
tcgetattr( fd, &oldstdio);
cfsetispeed(&oldstdio, B115200);
tcsetattr( fd, TCSANOW, &oldstdio);
tcflush( fd, TCIFLUSH ); num = serial_send( fd,"Serial BAUND is default \r\n" ); close(fd);
return ;
} int serial_send( int fd, char *Data )
{
int string_num;
string_num = strlen(Data);
return write( fd,Data, string_num );
}
在没有数据读取的时候,执行read函数会发生阻塞,执行下面的程序,在串口接收端没有数据时,返回0,并不会发生阻塞。
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h> const char *Serial_Dev = "/dev/ttyUSB0"; typedef struct {
char R_flag;
char W_flag;
int len;
char Data[];
}Serial; typedef struct {
int Forward;
int left;
int rotate;
unsigned char Check;
char Enter[];
}Vehicle; Vehicle Serial_Tx = {,,,,{"\r\n"}};
Serial Serial_D = {,,,{}};
int S_fd; int wait_flag = ; int serial_send( int fd, char *Data );
int set_opt(int fd,int nSpeed,int nBits,char nEvent,int nStop); void * Pthread_Serial( void *arg )
{
int n=;
int ret;
struct termios oldstdio;
char Rx_Data[];
char Tx_Data[]={}; S_fd = open( Serial_Dev, O_RDWR|O_NOCTTY );
if( -==S_fd )
pthread_exit(NULL); ret = set_opt(S_fd,,,'N',);
if(ret == -)
{
pthread_exit(NULL);
} while()
{
ret = read( S_fd, Rx_Data, );
if( ret > )
{
Serial_D.len = ret;
memset( Serial_D.Data, , Serial_D.len+ );
memcpy( Serial_D.Data, Rx_Data, Serial_D.len );
printf("%s",Serial_D.Data);
}
else
{
usleep();
sprintf( Tx_Data,"send %d\r\n", n++ );
serial_send( S_fd, Tx_Data );
//printf("send ok%d\r\n",n++);
}
}
pthread_exit(NULL);
} int main()
{
pthread_t pthread_id; //Create a thread
pthread_create( &pthread_id, NULL, &Pthread_Serial, NULL );
usleep(); if( -==S_fd )
{
printf("error: cannot open serial dev\r\n");
return -;
} while()
{
usleep(); } return ;
} int serial_send( int fd, char *Data )
{
int string_num;
string_num = strlen(Data);
return write( S_fd,Data, string_num );
} int set_opt(int fd,int nSpeed,int nBits,char nEvent,int nStop)
{
struct termios newtio,oldtio;
if(tcgetattr(fd,&oldtio)!=)
{
perror("error:SetupSerial 3\n");
return -;
}
bzero(&newtio,sizeof(newtio));
//使能串口接收
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE; newtio.c_lflag &=~ICANON;//原始模式 //newtio.c_lflag |=ICANON; //标准模式 //设置串口数据位
switch(nBits)
{
case :
newtio.c_cflag |= CS7;
break;
case :
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 :
cfsetispeed(&newtio,B2400);
cfsetospeed(&newtio,B2400);
break;
case :
cfsetispeed(&newtio,B4800);
cfsetospeed(&newtio,B4800);
break;
case :
cfsetispeed(&newtio,B9600);
cfsetospeed(&newtio,B9600);
break;
case :
cfsetispeed(&newtio,B115200);
cfsetospeed(&newtio,B115200);
break;
case :
cfsetispeed(&newtio,B460800);
cfsetospeed(&newtio,B460800);
break;
default:
cfsetispeed(&newtio,B9600);
cfsetospeed(&newtio,B9600);
break;
}
//设置停止位
if(nStop == )
newtio.c_cflag &= ~CSTOPB;
else if(nStop == )
newtio.c_cflag |= CSTOPB;
newtio.c_cc[VTIME] = ;
newtio.c_cc[VMIN] = ;
tcflush(fd,TCIFLUSH); if(tcsetattr(fd,TCSANOW,&newtio)!=)
{
perror("com set error\n");
return -;
}
return ;
}
可以使用select函数来判断有没有接收到数据。
int read_datas_tty(int fd,char *rcv_buf,int sec,int usec)
{
int retval;
unsigned char tempchar2;
fd_set rfds;
struct timeval tv;
int ret,pos; tv.tv_sec = sec;//set the rcv wait time
tv.tv_usec = usec;//100000us = 0.1s while()
{
FD_ZERO(&rfds);
FD_SET(fd,&rfds);
retval = select(fd+,&rfds,NULL,NULL,&tv);
if(retval ==-)
{
printf("select error\r\n");
break;
}
else if(retval)
{
ret= read(fd,rcv_buf,);
tempchar2 = rcv_buf;
printf("rcv_buf is %s\n",rcv_buf); }
else
{
break;
}
}
return ;
}
将上面的函数放到read前面调用即可。
下面是我用在小车上的代码:
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include "serial.h" const char *Serial_Dev = "/dev/ttyUSB0"; typedef struct {
int Forward;
int left;
int rotate;
unsigned char status;
unsigned char Check;
char Enter[];
}Vehicle; typedef struct {
int fd;
int sec;
int usec;
Vehicle* Veh;
}Uart; Vehicle Motor = {,,,,,{'\r','\n','\0'}};
Uart serial_usb={ -,,,NULL};; void * Pthread_Serial_Rx( void *arg )
{
int fd;
int retval;
struct timeval tv;
fd_set rfds;
Uart *ser = (Uart *)arg;
char Rx_data[];
int len;
int Num=; fd = ser->fd;
tv.tv_sec = ser->sec;
tv.tv_usec = ser->usec; while()
{
FD_ZERO(&rfds);
FD_SET( fd,&rfds );
retval = select(fd+,&rfds,NULL,NULL,&tv);
if( retval == - )
{
printf("error\r\n");
break;
}
else if( retval)
{
len = read(fd,Rx_data,);
// printf("read %d\r\n",len);
if( (len == )&&( Rx_data[] == 'S' ) )
{
if( Rx_data[] == '' )
ser->Veh->status = ;
else
ser->Veh->status = ;
Num=;
}
}
else
{
usleep();
Num++;
} if( Num>)
{
ser->Veh->status = ;
Num=;
}
}
pthread_exit(NULL); } void * Pthread_Serial( void *arg )
{
int n=;
int fd;
pthread_t pthread_id; fd = open( Serial_Dev, O_RDWR|O_NOCTTY );
if( -==fd )
pthread_exit(NULL); if( set_opt(fd,,,'N',)== -)
{
pthread_exit(NULL);
}
serial_usb.fd = fd;
serial_usb.sec = ;
serial_usb.usec = ;
serial_usb.Veh = &Motor; pthread_create( &pthread_id, NULL, &Pthread_Serial_Rx, ( void *)&serial_usb ); while( ==pthread_kill(pthread_id,) )
{
if(Motor.status)
{
Motor.Forward = ;
Motor.left = ;
Motor.rotate = ;
Motor.Check = (unsigned char)(Motor.Forward + Motor.left + Motor.rotate);
write( fd, &Motor, );
//serial_send( fd, "this is ok\r\n" );
} usleep();
} printf("receive thread is quited\r\n");
pthread_exit(NULL);
} int main()
{
pthread_t pthread_id; //Create a thread
pthread_create( &pthread_id, NULL, &Pthread_Serial, NULL );
usleep(); if( != pthread_kill(pthread_id,))
{
printf("error: cannot open serial dev\r\n");
return -;
}
printf("%d\r\n",sizeof(Vehicle));
//serial_send( serial_usb.fd, "this is ok\r\n" ); while( ==pthread_kill(pthread_id,) )
{
usleep();
if( Motor.status )
printf("The device is online %d\r\n",Motor.status);
else
printf("The device is offline\r\n");
}
printf("serial thread is quited\r\n");
return ;
}
sd
Linux下的串口编程及非阻塞模式的更多相关文章
- Linux下的串口编程实例
//串口相关的头文件 #include<stdio.h> /*标准输入输出定义*/ #include<stdlib.h> /*标准函数库定义*/ #in ...
- Linux下的串口编程(转)
https://blog.csdn.net/tigerjibo/article/details/6179291 #include<stdio.h> /*标准输入输出定义*/ #includ ...
- Linux下的C编程实战
Linux下的C编程实战(一) ――开发平台搭建 1.引言 Linux操作系统在服务器领域的应用和普及已经有较长的历史,这源于它的开源特点以及其超越Windows的安全性和稳定性.而近年来, Linu ...
- 详解linux下的串口通讯开发
串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使用.常用的串口是RS-232-C接口(又称EIA RS-232-C)它是在1970年由美国电子工业协会(EIA)联合贝尔系统.调制解调 ...
- 【转载】详解linux下的串口通讯开发
来源:https://www.cnblogs.com/sunyubo/archive/2010/09/26/2282116.html 串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使 ...
- socket异步通信-如何设置成非阻塞模式、非阻塞模式下判断connect成功(失败)、判断recv/recvfrom成功(失败)、判断send/sendto
socket异步通信-如何设置成非阻塞模式.非阻塞模式下判断connect成功(失败).判断recv/recvfrom成功(失败).判断send/sendto 博客分类: Linux Socket s ...
- 使用命名管道的OVERLAPPED方式实现非阻塞模式编程 .
命令管道是进程间通讯的一种常用方式,对于命令管道的介绍可以参考别的资料和书籍,这里推荐一个<VC++下命名管道编程的原理及实现>这篇博文,写得比较清楚.但是都是介绍了阻塞模式的编程,我这里 ...
- 服务器编程心得(四)—— 如何将socket设置为非阻塞模式
1. windows平台上无论利用socket()函数还是WSASocket()函数创建的socket都是阻塞模式的: SOCKET WSAAPI socket( _In_ int af, _In_ ...
- Linux下TCP网络编程与基于Windows下C#socket编程间通信
一.linux下TCP网络编程基础,需要了解相关函数 Socket():用于套接字初始化. Bind():将 socket 与本机上的一个端口绑定,就可以在该端口监听服务请求. Listen():使s ...
随机推荐
- 《HelloGitHub月刊》第09期
<HelloGitHub>第09期 兴趣是最好的老师,<HelloGitHub>就是帮你找到兴趣! 前言 转眼就到年底了,月刊做到了第09期,感谢大家一路的支持和帮助
- jQuery-1.9.1源码分析系列(十六)ajax——jsonp原理
json jsonp 类型 "json": 把响应的结果当作 JSON 执行,并返回一个JavaScript对象.如果指定的是json,响应结果作为一个对象,在传递给成功处理函数 ...
- [Asp.net 5] Caching-缓存架构与源码分析
首先奉献caching的开源地址[微软源码] 1.工程架构 为了提高程序效率,我们经常将一些不频繁修改,但是使用了还很大的数据进行缓存.尤其是互联网产品,缓存可以说是提升效率优化第一利器.微软为我们实 ...
- SQL Server 中怎么查看一个字母的ascii编码或者Unicode编码
参考文章:微信公众号文章 在sql中怎么查看一个字符的ascii编码,so easy !! select ASCII('a') SELECT CHAR(97) charNum SELECT UNICO ...
- 学习Redis你必须了解的数据结构——双向链表(JavaScript实现)
本文版权归博客园和作者吴双本人共同所有,转载和爬虫请注明原文链接 http://www.cnblogs.com/tdws/ 下午分享了JavaScript实现单向链表,晚上就来补充下双向链表吧.对链表 ...
- alienware Win8 系统安装
原作者网名 alienware-小来: 我的外星人 老是装系统出错.我觉得写的不错.把原作者的东西拿过来.. 对于win7系统的用户来说想要体验下win8.1系统,或者是原来win8.1系统加装固态后 ...
- js正则表达式整理
一.数字类 数字:^[0-9]*$ 正数.负数.和小数:^(\-|\+)?\d+(\.\d+)?$ 零和非零开头的数字:^(0|[1-9][0-9]*)$ 非零开头的最多带两位小数的数字:^([1-9 ...
- win2008R2 下解决关于mysql odbc无法正常工作问题
1.安装 mysql-connector-odbc-5.1.1-win32 下载 2.确保IIS中“启用32位应用程序”为True 3.连接字符串: mysql_connstr="Drive ...
- c#生成静态html文件,封装类
由于这段时间比较轻松,于是想到很多的企业网站,新闻网站需要将页面静态化,于是写了个封装类来实现静态文件的生成,思路比较简单,但未完善,网友可根据自己的思路将此类扩展,运用了简单工厂模式(本来刚开始看设 ...
- Eclipse安装Spring-tool-suite
目录结构: // contents structure [-] 在Eclipse上安装Spring-tool-suite的方法有那些 如何查看自己的Eclipse版本 如何知道自己的Eclipse对应 ...