linux下实现串口通讯
来源:互联网 发布:艾滋病感染概率 知乎 编辑:程序博客网 时间:2024/06/05 05:36
1、关键结构体
(1)struct termios
{
unsigned short c_iflag; /* 输入模式标志*/
unsigned short c_oflag; /* 输出模式标志*/
unsigned short c_cflag; /* 控制模式标志*/
unsigned short c_lflag; /*区域模式标志或本地模式标志或局部模式*/
unsigned char c_line; /*行控制line discipline */
unsigned char c_cc[NCC]; /* 控制字符特性*/
};
(一)c_iflag 标志常量:
Input mode ( 输入模式)
input mode可以在输入值传给程序之前控制其处理的方式。
其中输入值可能是由序列埠或键盘的终端驱动程序所接收到的字元。
我们可以利用termios结构的c_iflag的标志来加以控制,其定义的方式皆以OR来加
以组合。
*
* IGNBRK :忽略输入中的 BREAK 状态。 (忽略命 令行中的中
断)
* BRKINT :(命令行出 现中断时,可产生一插断)如果设置了
IGNBRK,将忽略 BREAK。如果没有设置,但是设置了 BRKINT,
那么 BREAK 将使得输入和输出队列被刷新,如果终端是一个前
台进程组的控制终端,这个进程组中所有进程将收到 SIGINT 信
号。如果既未设置 IGNBRK 也未设置 BRKINT,BREAK 将视为与
NUL 字符同义,除非设置了 PARMRK,这种情况下它被视为序列
377 � �。
* IGNPAR :忽略桢错误和奇偶校验错。
* PARMRK :如果没有设置 IGNPAR,在有奇偶校验错或桢错误的字
符前插入 377 �。如果既没有设置 IGNPAR 也没有设置
PARMRK,将有奇偶校验错或桢错误的字符视为 �。
* INPCK :启用输入奇偶检测。
* ISTRIP :去掉第八位。
* INLCR :将输入中的 NL 翻译为 CR。(将收到 的换行符号转换
为Return)
* IGNCR :忽略输入中的回车。
* ICRNL :将输入中的回车翻译为新行 (除非设置了 IGNCR)(否则
当输入信号有 CR 时不会终止输入)。
* IUCLC :(不属于 POSIX) 将输入中的大写字母映射为小写字
母。
* IXON :启用输出的 XON/XOFF 流控制。
* IXANY :(不属于 POSIX.1;XSI) 允许任何字符来重新开始输
出。(?)
* IXOFF :启用输入的 XON/XOFF 流控制。
* IMAXBEL:(不属于 POSIX) 当输入队列满时响零。Linux 没有实
现这一位,总是将它视为已设置。
(二) c_oflag 标志常量:Output mode ( 输 出模式)
Output mode主要负责控制输出字元的处理方式。输出字元在传送到序列埠或显示
器之前是如何被程序来处理。
输出模式是利用termios结构的c_oflag的标志来加以控制,其定义的方式皆以OR来
加以组合。
*
* OPOST :启用具体实现自行定义的输出处理。
* OLCUC :(不属于 POSIX) 将输出中的小写字母映射为大写字
母。
* ONLCR :(XSI) 将输出中的新行符映射为回车-换行。
* OCRNL :将输出中的回车映射为新行符
* ONOCR :不在第 0 列输出回车。
* ONLRET :不输出回车。
* OFILL :发送填充字符作为延时,而不是使用定时来延时。
* OFDEL :(不属于 POSIX) 填充字符是 ASCII DEL (0177)。如果
不设置,填充字符则是 ASCII NUL。
* NLDLY :新行延时掩码。取值为 NL0 和 NL1。
* CRDLY :回车延时掩码。取值为 CR0, CR1, CR2, 或 CR3。
* TABDLY :水平跳格延时掩码。取值为 TAB0, TAB1, TAB2, TAB3
(或 XTABS)。取值为 TAB3,即 XTABS,将扩展跳格为空格 (每
个跳格符填充 8 个空格)。(?)
* BSDLY :回退延时掩码。取值为 BS0 或 BS1。(从来没有被实现
过)
* VTDLY :竖直跳格延时掩码。取值为 VT0 或 VT1。
* FFDLY :进表延时掩码。取值为 FF0 或 FF1。
(三)c_cflag 标志常量:Control mode ( 控制模式)
Control mode主要用于控制终端设备的硬件设置。利用termios结构的c_cflag的标
志来加以控制。控制模式用在序列线连接到数据设备,也可以用在与终 端设备的
交谈。
一般来说,改变终端设备的组 态要比使用termios的控制模式来改变行(lines)的
行为来得容易。
*
* CBAUD :(不属于 POSIX) 波特率掩码 (4+1 位)。
* CBAUDEX :(不属于 POSIX) 扩展的波特率掩码 (1 位),包含在
CBAUD 中。
* (POSIX 规定波特率存储在 termios 结构中,并未精确指定它的
位置,而是提供了函数 cfgetispeed() 和 cfsetispeed() 来存
取它。一些系统使用 c_cflag 中 CBAUD 选择的位,其他系统使
用单独的变量,例如 sg_ispeed 和 sg_ospeed 。)
* CSIZE:字符长度掩码(传送或接收字元时用的位数)。 取值为
CS5(传送或接收字元时用5bits), CS6, CS7, 或 CS8。
* CSTOPB :设置两个停止位,而不是一个。
* CREAD :打开接受者。
* PARENB :允许输出产生奇偶信息以及输入的奇偶校验(启用同
位产生与侦测)。
* PARODD :输入和输出是奇校验(使用奇同位而非偶同位)。
* HUPCL :在最后一个进程关闭设备后,降低 modem 控制线 (挂
断)。(?)
* CLOCAL :忽略 modem 控制线。
* LOBLK :(不属于 POSIX) 从非当前 shell 层阻塞输出(用于
shl )。(?)
* CIBAUD :(不属于 POSIX) 输入速度的掩码。CIBAUD 各位的值与
CBAUD 各位相同,左移了 IBSHIFT 位。
* CRTSCTS :(不属于 POSIX) 启用 RTS/CTS (硬件) 流控制。
2、串口通讯的关键函数
(1)tcgetattr
1.原型
int tcgetattr(int fd,struct termois & termios_p);
2.功能
取得终端介质(fd)初始值,并把其值 赋给temios_p;函数可以从后台进
程中调用;但是,终端属性可能被后来的前 台进程所改变。
(2)tcflush
tcflush函数刷清(扔掉)输入缓存(终端驱动法度已接管到,但用户法度尚未读)或输出缓存(用户法度已经写,但尚未发送).
int tcflush(int filedes,int quene)
quene数该当是下列三个常数之一:
*TCIFLUSH 刷清输入队列
*TCOFLUSH 刷清输出队列
*TCIOFLUSH 刷清输入、输出队列
(3)tcsetattr
1.原型
int tcsetattr(int fd,int actions,const struct termios
*termios_p);
2.功能
设置与终端相关的参数 (除非需要底层支持却无法满足),使用
termios_p 引用的 termios 结构。optional_actions (tcsetattr函数
的第二个参数)指定了什么时候改变会起作用:
* TCSANOW:改变立即发生
* TCSADRAIN:改变在所有写入 fd 的输出都被传输后生效。这个
函数应当用于修改影响输出的参数时使用。(当前输出完成时将
值改变)
* TCSAFLUSH :改变在所有写入 fd 引用的对象的输出都被传输后
生效,所有已接受但未读入的输入都在改变发生前丢弃(同
TCSADRAIN,但会舍弃当前所有值)。
3、串口通讯波特率设置
波特率的设置定义在<asm/termbits.h>,其包含在头文件<termios.h>里。
常用的波特率常数如下:
B0 ----- 0 B1800 ----- 1800
B50 ----- 50 B2400 ----- 2400
B75 ----- 75 B4800 ----- 4800
B110 ----- 110 B9600 ----- 9600
B134 ----- 134.5 B19200 ----- 19200
B200 ----- 200 B38400 ----- 38400
B300 ----- 300 B57600 ----- 57600
B600 ----- 600 B76800 ----- 76800
B1200----- 1200 B115200 ----- 115200
假定程序中想要设置通讯的波特率,使用cfsetispeed( )和cfsetospeed( )函数来操作,获取波特率信息是通过cfgetispeed()和cfgetospeed()函数来完成的。比如可以这样来指定串口通讯的波特率:
#i nclude <stdio.h> //头文件定义
.......
struct termios opt; /*定义指向termios 结构类型的指针opt*/
/***************以下设置通讯波特率****************/
cfsetispeed(&opt,B115200 ); /*指定输入波特率,115200bps*/
cfsetospeed(&opt,B115200);/*指定输出波特率,115200bps*/
/************************************************/
..........
一般来说,输入、输出的波特率应该是一致的。
3、代码实现
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <termios.h>
#include "serial_comm.h"
int OpenDev(void)
{
int fd = -1;
printf("%s\n", SERIAL_DEV);
fd = open(SERIAL_DEV, O_RDWR | O_NOCTTY | O_NDELAY);
if ( -1 == fd)
{
printf("%s, %d, open failed\n", __FUNCTION__, __LINE__);
return -1;
}
printf("%s, fd = %d\n", SERIAL_DEV, fd);
return fd;
}
int SetSerialParam(int fd,int databits,int stopbits,int parity)
{
struct termios options;
if ( tcgetattr( fd,&options) != 0) {
printf("%s, %d, tcgetattr failed\n", __FUNCTION__, __LINE__);
return -1;
}
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
options.c_cflag &= ~CSIZE;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
options.c_oflag &= ~OPOST; /*Output*/
switch (databits) /*设置数据位数*/
{
case 7:
options.c_cflag |= CS7;
break;
case 8:
printf("%s, %d, 8bit\n", __FUNCTION__, __LINE__);
options.c_cflag |= CS8;
break;
default:
fprintf(stderr,"Unsupported data size/n");
return -1;
}
switch (parity)
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
printf("%s, %d, no parity\n", __FUNCTION__, __LINE__);
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* 转换为偶效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
fprintf(stderr,"Unsupported parity/n");
return -1;
}
/* 设置停止位*/
switch (stopbits)
{
case 1:
printf("%s, %d, no parity\n", __FUNCTION__, __LINE__);
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
fprintf(stderr,"Unsupported stop bits/n");
return -1;
}
/* Set input parity option */
if (parity != 'n')
options.c_iflag |= INPCK;
/*下面是设置read读的字节个数我本身用不到干掉*/
#if 0
options.c_cc[VTIME] = 0; /* 设置超时0 seconds*/
options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/
#endif
printf("%s, %d, \n", __FUNCTION__, __LINE__);
/*清空缓冲区*/
tcflush(fd,TCIFLUSH);
/*设置终端参数*/
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
printf("%s, %d, tcsetattr failed\n", __FUNCTION__, __LINE__);
return -1;
}
printf("%s, %d, set serial success\n", __FUNCTION__, __LINE__);
return 0;
}
int SerialSend(int fd, char *scData, int DataLen)
{
int retval = -1;
int iRelSendlen = 0,size;
if (NULL == scData || fd < 0)
{
printf("%s, %d, Input Param Failed\n", __FUNCTION__, __LINE__);
return -1;
}
printf("%s, %d, scData = %s, DataLen = %d\n", __FUNCTION__, __LINE__, scData, DataLen);
while (iRelSendlen != DataLen)
{
size = write(fd, scData+iRelSendlen, DataLen-iRelSendlen);
printf("%s, %d, size = %d\n", __FUNCTION__, __LINE__, size);
if (size <= 0)
{
printf("%s, %d, Serial Send Failed\n", __FUNCTION__, __LINE__);
break;
}
iRelSendlen += size;
}
return 0;
}
int SerialRead(int fd, char *Data, int DataLen, int TimeOut, unsigned long PerTimeOut)
{
struct timeval tn, ti, diff, td, timeout;
unsigned int uiReadCnt=0;
int retval,ibacklight_set_time_bak;
if (fd < 0 || !Data)
{
return -1;
}
diff.tv_sec = TimeOut;
diff.tv_usec = 0;
gettimeofday(&tn, NULL); //获取当前时间
timeradd(&tn, &diff, &ti);
while (1)
{
fd_set inset;
int ireaded = 0;
int error = -1;
if (uiReadCnt == DataLen) {
retval = uiReadCnt;
break;
}
FD_ZERO(&inset);
FD_SET(fd, &inset);
if (!TimeOut)
timeout.tv_sec = timeout.tv_usec = 0;
else
{
gettimeofday(&tn, NULL);
if (timercmp(&tn, &ti, >=))
{
printf("%s, %d, Timeout\n", __FUNCTION__, __LINE__);
break;
}
diff.tv_sec = 0;
diff.tv_usec = PerTimeOut * 1000;
timeradd(&tn, &diff, &td);
if (timercmp(&ti, &td, >))
timeout = diff;
else
timersub(&ti, &tn, &timeout);
}
// 检测接收
error = select(fd+1, &inset, NULL, NULL, &timeout);
if (error < 0)
{ // 失败
printf("%s, %d, select Failed\n", __FUNCTION__, __LINE__);
break;
}
if (FD_ISSET(fd, &inset))
{
ireaded = read(fd, Data+uiReadCnt, DataLen-uiReadCnt);
printf("%s, %d, Data = %s\n", __FUNCTION__, __LINE__, Data);
if (ireaded < 0)
{
printf("%s, %d, Read Over\n", __FUNCTION__, __LINE__);
retval = uiReadCnt;
break;
}
uiReadCnt += ireaded;
}
else
{
if (uiReadCnt > 0)
{
printf("%s, %d, uiReadCnt = %d\n", __FUNCTION__, __LINE__, uiReadCnt);
retval = uiReadCnt;
break;
}
else if (! TimeOut)
{
printf("%s, %d, uiReadCnt = %d\n", __FUNCTION__, __LINE__, uiReadCnt);
break;
}
}
}
return retval;
}
int SerialClose(int fd)
{
int iRet = 0;
/*先清空缓冲区,在关闭串口*/
tcflush(fd,TCIFLUSH);
iRet = close(fd);
return iRet;
}
static void *Proc_SerialRead(void *arg)
{
int fd = *(int *)arg;
printf("%s, %d, fd = %d\n", __FUNCTION__, __LINE__, fd);
while (1)
{
char acGetData[100] = {0};
SerialRead(fd,acGetData, sizeof(acGetData), 3, 1);
usleep(500*1000);
printf("%s, %d, acGetData = %s\n", __FUNCTION__, __LINE__, acGetData);
}
return (void *)0;
}
int main(void)
{
int iRet = -1;
int fd = -1;
static pthread_t iThreadID = -1;
char *scData = "111111111122314131341313413414141341341341";
fd = OpenDev();
if (-1 == fd)
{
printf("%s, %d, OpenDev Failed\n", __FUNCTION__, __LINE__);
return 0;
}
printf("%s, %d, fd = %d\n", __FUNCTION__, __LINE__, fd);
iRet = SetSerialParam(fd, 8, 1, 'n');
printf("%s, %d, iRet = %d\n", __FUNCTION__, __LINE__, iRet);
if (!pthread_create(&iThreadID, NULL, Proc_SerialRead, &fd));
while(1)
{
SerialSend(fd, scData, strlen(scData) );
sleep(1);
}
SerialClose(fd);
return 0;
}
函数的构成模块主要有(1)打开设备(2)设置设备参数(3)读数据(4)写数据即发送数据(5)关闭设备。主函数中创建一个线程循环接收数据,主线程循环发送数据。
此代码已经编译自测过,可以正常工作。
- linux下实现串口通讯
- Linux 下串口通讯编程
- Linux 下串口通讯编程
- linux下串口通讯参数设置
- linux下串口通讯参数设置
- Linux下串口通讯编程
- mac下如何实现串口通讯
- mac下如何实现串口通讯
- linux下串口通讯相关资料
- 详解linux下的串口通讯开发
- 详解linux下的串口通讯开发
- 详解linux下的串口通讯开发
- 详解linux下的串口通讯开发
- 详解linux下的串口通讯开发
- 详解linux下的串口通讯开发
- Linux系统下串口通讯编程
- 详解linux下的串口通讯开发
- 详解linux下的串口通讯开发
- smtp 发送邮件 乱码解决
- getContextPath、getServletPath、getRequestURI的区别
- uva11396 - Claw Decomposition
- 多线程时钟模拟
- leetcode之Insertion Sort List
- linux下实现串口通讯
- JSF —— Facelets 标签
- HDU-1043 Eight 八数码问题
- Spirng3基于注解(annotation)整合ehcache 使用页面缓存、对象缓存
- 数据库索引的作用和优点缺点
- 最小步数nyoj--92
- 算法实战5:多种数据结构实现四则运算
- 相似度计算
- Win7 安装Apache 2.2.4报错:<OS 5>拒绝访问. :Failed to open the WinNT service manager