storysnail的Linux串口编程笔记
storysnail的Linux串口编程笔记
作者 |
He |
||||||||
团队 |
ls |
||||||||
版权 |
转载请保留本声明! 本文档包含的原创代码根据General 本文档根据GNU 文中所引用的软件版权详见各软件版权具体声明,文中所提及的所有商标均为各自商标所有人的财产。 |
||||||||
更新 |
|
前言:
这半个月因肺部感染而不得不暂时终止那令人生厌的中石油巡检工作,闭门在家安静的
修养。整月的工钱自然是泡汤了,可却得来了极其珍贵的个人闲暇时光,让我能淋漓尽致的
做软件方面的研究,虽是粗茶淡饭,针剂苦药,但可静心埋头于书房,却比天堂还甜美!
恍惚已至月末,工作单位来了音讯,让我一下子从甜美的梦中惊醒,从哪里来,回哪里
去,这种如"主体思想"一样可怕的思维是我挥之不去的梦魇,无奈、不知所措、病弱的身体
却不由自主的向那发声的地方靠去!
好了,还是不再发牢骚了,只是个人觉得这种臃肿低效的国企能够存在,本身就是对“
国富论”绝佳的嘲讽,我只能用世界是多元的来啊Q一下了!
切入正题,这段时间做GSM/GPRS和GPS的小东西,需要通过串口发送AT指令来控制,以前
调试一直在用串口助手和minicom之类的现成软件,可是一点都不爽,为什么不自己写个操作
串口的软件,就像在ARM和stm32上一样!
这文章其实只是我的一个笔记,分为两篇,一篇是《storysnail的Windows串口编程笔记》,
另一姊妹篇是《storysnail的Linux串口编程笔记》,由于网上已经有非常多的类似文章,有些长篇
大论,有些短小精悍,连我自己都思考过是否有必要再写一篇,但在Ling的鼓动下还是写了!
本篇是Linux串口编程笔记,详细介绍了串口通信会用到的api函数,并提供了一个示例程序,
这个示例程序是在EEEPC701的debian系统上编写测试的。
一:写串口程序用到的函数
1:Linux与windows串口设备文件名对照
操作系统 |
USB/RS-232转换器 |
||
Windows |
COM1 |
COM2 |
COMX(我的系统上X=4) |
Linux |
/dev/ttyS0 |
/dev/ttyS1 |
/dev/ttyUSB0 |
2:写串口程序用到的函数
串行通讯函数定义在termios.h头文件中,所以需要包含该文件。下面是要介绍的函数列表
函数名 |
功能 |
open |
打开串口 |
close |
关闭串口 |
read |
接收数据 |
write |
发送数据 |
fcntl |
设置IO为阻塞或非阻塞 |
ioctl |
实现POSIX.1 |
tcgetattr |
读取串口设备的当前属性,保存在termios_p所指向的结构中 |
tcsetattr |
设置串口设备的当前属性 |
cfgetospeed |
返回输出波特率 |
cfgetispeed |
返回输入波特率 |
cfsetispeed |
设定输入波特率 |
cfsetospeed |
设定输出波特率 |
2.1
open
用途:打开串口
原型:int
open(
const
char
*
pathname,int
flags);
参数说明:
pathname:
指向欲打开的文件路径字符串flags
所能使用的标志位:
O_RDONLY |
以只读方式打开文件 |
O_WRONLY |
以只写方式打开文件 |
O_RDWR |
以可读写方式打开文件。 O_RDONLY、O_WRONLY、O_RDWR标志位是互斥的,不可同时使用,但可与下列的标志位|运算组合。 |
O_CREAT |
若欲打开的文件不存在则自动建立该文件。 |
O_EXCL |
如果O_CREAT |
O_NOCTTY |
表明本程序不是该串口上的“控制终端”。即本程序不受Ctrl+c、Ctrl+z这类组合键产生的信号影响。 |
O_TRUNC |
若文件存在并且以可写的方式打开时,此标志位会令文件长度清为0,而原来存于该文件的 |
O_APPEND |
当读写文件时会从文件尾开始移动,也就是所写入的数据会以附加的方式加入到文件后面。 |
O_NONBLOCK |
非阻塞模式打开。在打开很多串行端口设备时,open函数有时候会阻塞很长一段时间.例如当打开一个调制解调器的端口就会阻塞直到DCD信号线有信号电压为止, |
O_NDELAY |
其实和O_NONBLOCK基本相同,所产生的结果都是使I/O变成非阻塞模式,唯一的一点差别是O_NDELAY会让函数马上返回0,而O_NONBLOCK在读不到数据时会返回-1,并且设置errno为EAGAIN。在GNU #define |
O_SYNC |
以同步的方式打开文件。 |
O_NOFOLLOW |
如果参数pathname |
O_DIRECTORY |
如果参数pathname |
FNDELAY |
FNDELAY,实际上在fcntl.h中是使用O_NDELAY作为宏定义. #define |
举例:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<termios.h>
intCom_Open(void)
{
;
fd=open("/dev/ttyUSB0",O_RDWR|O_NOCTTY|O_NONBLOCK);
){
perror("open_port:
Unable to open /dev/ttyUSB0");
}
)
printf("IO
set error\n");
return(fd);
}
2.2
close
用途:关闭串口
原型:int
close(
int
fd);
参数说明:
fd:
文件描述符,关闭串口后计算机会将DTR信号线设置成低电位,这会告诉另一端的设备你的计算机状态。
举例:
intCom_Close(intfd)
{
)
;
)
;
printf("close
uart\n\n");
;
}
2.3
read
用途:接收数据
原型:ssize_t
read(int
fd,
void
*buf,
ssize_t
nbyte);
参数说明:
fd:
文件描述符buffer:读取缓冲区
number:要读多少个字节,不能大于buf指向的缓冲区大小
举例:
n
=
read(fd,buf,sizeof(buf));
2.4
write
用途:发送数据
原型:ssize_t
write
(int
fd,const
void
*
buf,size_t
count);
参数说明:
fd:
文件描述符buffer:写入缓冲区
count:要写多少个字节
函数返回:
write函数也会返回发送数据的字节数或者在发生错误的时候返回-1。通常,发送数据最常见的错误就是EIO,当调制解调器或者数据链路将Data
Carrier Detect(DCD)
信号线弄掉了,就会发生这个错误。而且,直至关闭端口这个情况会一直持续。
举例:
);
)
perror("write()
of 4 bytes failed!\n",stderr);
2.5
fcntl
用途:设置IO为阻塞或非阻塞
原型:int
fcntl(int
fd,int
cmd,...);
参数说明:略
举例:
intCom_SetIOBlockFlag(intfd,intvalue)
{
intoldflags;
)
;
);
){
printf("get
IO flags error\n");
;
}
if(value==BLOCK_IO)
oldflags&=~O_NONBLOCK;//设置成阻塞IO
else
oldflags|=O_NONBLOCK;//设置成非阻塞IO
returnfcntl(fd,F_GETFL,oldflags);
}
2.6
ioctl
用途:
实现POSIX.1
GTI控制界面所有函数功能,配置串口不仅仅可以使用上面说的方法,在Linux环境下,还可以使用ioctl系统调用来实现,写过驱动程序的人都知道,ioctl是个大口袋,任何IO操作都可以交给它。
原型:int
ioctl(int
fd,
int
request,
...);
参数说明:
fd:
串口设备文件的文件描述符。request:
参数在asm-generic/ioctl.h头文件中定义
串口设置
TCGETS
读取当前的串口属性
同功能函数:tcgetattr
TCSETS
设置串口属性并立即生效
同功能函数:tcsetattr(fd,
TCSANOW, &options)
TCSETSF
设置串口属性,等到输入输出队列都清空了再生效
同功能函数:tcsetattr(fd,
TCSAFLUSH, &options)
TCSETSW
设置串口属性,等到输入输出队列都清空或传输完成了再生效
同功能函数:tcsetattr(fd,
TCSADRAIN, &options)
TCSBRK
在指定时间后发送break
同功能函数:tcsendbreak
TCXONC
控制软件流控制
同功能函数:tcflow
TCFLSH
丢弃输入输出队列中尚未传送或读取的数据!
同功能函数:tcflush
注意:tcflush这个函数的命名就是个灾难,因为"flush"在linux中用于
描述“等待直至所有输入输出全部传送完毕”,例如:fflush
FIONREAD
返回输入队列中的字节数
这4个IO控制命令用于获取和设置MODEM握手,如RTS、CTS、DTR、DSR、RI、CD等,不过有些嵌入式设备的uart
并不包括完整的MODEM控制信号线
TIOCMGET
获取MODEM状态位
TIOCMSET
设置MODEM状态位
TIOCMBIC
清除指示MODEM的位
TIOCMBIS
设置指示MODEM的位
获得串口控制信号
TIOCM_LE
DSR (data set ready/line enable)
TIOCM_DTR
DTR (data terminal ready)
TIOCM_RTS
RTS (request to send)
TIOCM_ST
Secondary TXD (transmit)
TIOCM_SR
Secondary RXD (receive)
TIOCM_CTS
CTS (clear to send)
TIOCM_CAR
DCD (data carrier detect)
TIOCM_CD
Synonym for TIOCM_CAR
TIOCM_RNG
RNG (ring)
TIOCM_RI
Synonym for TIOCM_RNG
TIOCM_DSR
DSR (data set ready)
举例:
//获取MODEM状态位
intfd;
intstatus;
ioctl(fd,TIOCMGET,&status);
//设置MODEM状态位,将DTR信号线设成掉线状态。
intfd;
intstatus;
ioctl(fd,TIOCMGET,&status);
status&=~TIOCM_DTR;
ioctl(fd,TIOCMSET,&status);
//得到串口输入队列中的字节数
intfd;
intbytes;
ioctl(fd,FIONREAD,&bytes);
2.7
tcgetattr()
用途:读取串口设备的当前属性,保存在termios_p所指向的结构中
原型:int
tcgetattr
(int
fd,
struct
termios
*termios_p);
参数说明:
fd:
串口设备描述符termios_p:
termios结构体指针
2.8
tcsetattr()
用途:设置串口设备的当前属性
原型:int
tcsetattr
(int
fd,
int
action,const
struct
termios
*termios_p);
参数说明:
fd:
串口设备描述符action:
TCSANOW 立即做出改变
TCSADRAIN
等到输入输出队列都清空了再生效
TCSAFLUSH
等到输入输出队列都清空或传输完成了再生效
termios_p:
termios结构体指针
2.9
cfgetospeed()
用途:返回输出波特率
原型:speed_t
cfgetospeed
(const
struct
termios
*termios_p);
参数说明:
termios_p:
termios结构体指针
2.10
cfgetispeed()
用途:返回输入波特率
原型:speed_t
cfgetispeed
(const
struct
termios
*termios_p);
参数说明:
termios_p:
termios结构体指针
2.11
cfsetispeed()
用途:设定输入波特率
原型:int
cfsetispeed
(struct
termios
*termios_p,
speed_t
speed);
参数说明:
termios_p:
termios结构体指针speed:
见cfsetispeed()函数
2.12
cfsetospeed()
用途:设定输出波特率
原型:int
cfsetospeed
(struct
termios
*termios_p,
speed_t
speed);
参数说明:
termios_p:
termios结构体指针speed:
详见bits/termios.h头文件,这里只列出几个常用值
#define
B1200
#define
B1800
#define
B2400
#define
B4800
#define
B9600
#define
B19200
#define
B38400
#define
B57600
#define
B115200
举例:
structtermiosoptions;
speed_tInSpeed,OutSpeed;
tcgetattr(fd,&options);
InSpeed=cfgetispeed(&options);
OutSpeed=cfgetospeed(&options);
//设置输入和输出波特率为115200
cfsetispeed(&options,B115200);
cfsetospeed(&options,B115200);
tcsetattr(fd,TCSANOW,&options);
二 配置串口 --
POSIX.1 GTI控制界面
POSIX.1
GTI定义在termios.h和bits/termios.h头文件中,由一个termios结构体和12个函数构成,这里只介绍了部分内容,更多内容可参考本文结尾提到的书籍!
1:termios结构
typedefunsignedcharcc_t;
typedefunsignedintspeed_t;
typedefunsignedinttcflag_t;
structtermios
{
tcflag_tc_iflag;/*输入方式标志
*/
tcflag_tc_oflag;/*输出方式标志
*/
tcflag_tc_cflag;/*控制方式标志
*/
tcflag_tc_lflag;/*局部方式标志
*/
cc_tc_line;/*
line discipline */
cc_tc_cc[NCCS];/*控制字符数组
*/
speed_tc_ispeed;/*输入波特率
*/
speed_tc_ospeed;/*输出波特率
*/
};
2:termios结构体的c_cflag成员
termios结构体的c_cflag描述基本的串口硬件控制,像波特率、是否激活奇偶校验检查、校验方式、一个字节的数据位个数、停止位个数和硬件流控制等。所有的都是位操作,需要使用位运算的与或非组合来设置或者清除相关标志。
//一个字节的数据位个数
//一个字节的数据位个数为5
//一个字节的数据位个数为6
//一个字节的数据位个数为7
//一个字节的数据位个数为8
//停止位个数2,否则为1
不理解的参见下面的代码
//启用接收器
//激活奇偶校验检查
//校验方式为奇效验,否则为偶效验
//最后一次关闭时挂断,即将DTR信号线设置成低电位
//忽略状态线,没有流控制
//启用硬件流控制,注意软件流控是在c_iflag中设置的
设置一个字节的数据位个数
options.c_flag&=~CSIZE;/*Mask
the character size bits */
options.c_flag|=CS8;/*Select
8 data bits */
设置奇偶校验
//
无奇偶校验
(8N1)
options.c_cflag&=~PARENB
options.c_cflag&=~CSTOPB
options.c_cflag&=~CSIZE;
options.c_cflag|=CS8;
//
偶校验(7E1)
options.c_cflag|=PARENB
options.c_cflag&=~PARODD
options.c_cflag&=~CSTOPB
options.c_cflag&=~CSIZE;
options.c_cflag|=CS7;
//
奇校验(7O1)
options.c_cflag|=PARENB
options.c_cflag|=PARODD
options.c_cflag&=~CSTOPB
options.c_cflag&=~CSIZE;
options.c_cflag|=CS7;
//
Mark位停止位来模拟(7M1)
options.c_cflag&=~PARENB
options.c_cflag|=CSTOPB
options.c_cflag&=~CSIZE;
options.c_cflag|=CS7;
//
Space校验
,这与无奇偶校验相同(7S1)
options.c_cflag&=~PARENB
options.c_cflag&=~CSTOPB
options.c_cflag&=~CSIZE;
options.c_cflag|=CS8;
设置硬件流控制
//
启用硬件流控制
options.c_cflag|=CRTSCTS;/*Also
called CRTSCTS
//
停用硬件流控制
options.c_cflag&=~CRTSCTS;
3:termios结构体的c_lflag成员
termios结构体的c_lflag用来设置串口驱动与用户之间的界面,例如打开还是关闭回显,是否显示ERASE字符,使用加工方式输入还是使用非加工方式输入。由于我只需要得到原始数据,所以只需要将termios结构体的c_lflag置0即可。
ISIG |
启用 SIGINTR, |
ICANON |
启用加工方式输入,否则使用非加工方式输入 |
XCASE |
启用加工大小写 |
ECHO |
启用回显 |
ECHOE |
用BS-SP-BS回显擦写字符(ERASE) |
ECHOK |
回显KILL字符 |
ECHONL |
回显NL字符 |
NOFLSH |
禁止中断、退出或终止后清除输出队列 |
IEXTEN |
启用实现定义的功能 |
ECHOCTL |
回显控制字符,例如 ^和 |
ECHOKE |
通过擦去屏幕上的字符回显KILL |
FLUSHO |
输出被清除 |
PENDIN |
重新打印悬挂的输出 |
TOSTOP |
为后台输出发送SIGTTOU信号 |
设置非加工方式输入,在非加工方式下,原始输入根本不会被处理。输入字符只是被原封不动的接收。
options.c_lflag&=~(ICANON|ECHO|ECHOE|ISIG);
设置加工方式输入
options.c_lflag|=(ICANON|ECHO|ECHOE);
注意:当发送数据给调制解调器或其它计算机时,如果它们对发送的数据启用回显,则发送端千万不要启用回显,那样会导致两个连接的串口之间生成信息反馈循环。
4:termios结构体的c_oflag成员
termios结构体的c_oflag用来控制串口的数据输出例如转换换行符CR/LF等,或选择使用加工方式输出还是使用非加工方式输出。和上面一样,我只需要得到原始数据,所以只需要将termios结构体的c_oflag置0即可。
OPOST |
启用加工方式输出,否则使用非加工方式输出 |
OLCUC |
转换输出时的小写字符为大写字符 |
ONLCR |
将换行字符(NL)转换成回车换行字符(CR-NL) |
OCRNL |
将回车字符(CR)转换成换行字符(NL) |
NOCR |
No |
ONLRET |
NL |
OFILL |
Use |
OFDEL |
Fill |
NLDLY |
Mask |
NL0 |
No |
NL1 |
Delay |
CRDLY |
Mask |
CR0 |
No |
CR1 |
Delay |
CR2 |
Delay |
CR3 |
Delay |
TABDLY |
Mask |
TAB0 |
No |
TAB1 |
Delay |
TAB2 |
Delay |
TAB3 |
Expand |
BSDLY |
Mask |
BS0 |
No |
BS1 |
Delay |
VTDLY |
Mask |
VT0 |
No |
VT1 |
Delay |
FFDLY |
Mask |
FF0 |
No |
FF1 |
Delay |
设置成使用非加工方式输出,当然c_oflag中其它选项都会失效
options.c_oflag&=~OPOST;
5:termios结构体的c_iflag成员
termios结构体的c_iflag用来控制串口的数据输入,例如剥离输入字符为8位,使奇偶效验生效等
c_iflag成员可以使用的常量
常量 |
描述 |
INPCK |
启用奇偶效验检查 |
IGNPAR |
忽略奇偶效验错误 |
PARMRK |
标识奇偶效验出错的数据 |
ISTRIP |
剥去输入字符至7位 |
IXON |
启用输出软件流控制 |
IXOFF |
启用输入软件流控制 |
IXANY |
启用任何输入字符回复暂停的输出 |
IGNBRK |
忽略输入行的终止条件 |
BRKINT |
当输入行中监测到终止条件时发送SIGINT信号 |
INLCR |
将换行字符(NL)转换成回车字符(CR) |
IGNCR |
忽略CR |
ICRNL |
将回车字符(CR)转换成换行字符(NL) |
IUCLC |
将大写字符转换成小写字符 |
IMAXBEL |
输入队列满时响铃 |
启用奇偶效验检查并从接收字符串中脱去奇偶校验位:
options.c_iflag|=(INPCK|ISTRIP);
启用IGNPAR会忽略奇偶效验错误并给数据放行。如果你使用8N1模式,可以考虑开启该功能。
当启用INPCK后,启用PARMRK会标识奇偶效验出错的数据。设该数据为'X',如果启用IGNPAR,那么一个NUL('\0')字符会被加入到发生奇偶校验错误的字符前面,即'\0''X'。否则,DEL('\377',注意这是8进制)和NUL('\0')字符会和出错的字符一起送出,即'\377''\0''X'。
启用软件流控制
options.c_iflag|=(IXON|IXOFF|IXANY);
禁用软件流控制
options.c_iflag&=~(IXON|IXOFF|IXANY);
6:termios结构体的c_cc成员
termios结构体的c_cc里面包括了控制字符的定义和超时参数。
c_cc数组在加工方式和非加工方式下的作用不同,作为c_cc数组索引的宏常量也分为两种情况使用,
并且索引值在两种加工方式下有部分重叠,所以一定要区分对待。
#define
VMIN
#define
VSWTC
#define
VSTART
#define
VSTOP
#define
VSUSP
#define
VEOL
#define
VREPRINT
#define
VDISCARD
#define
VWERASE
#define
VLNEXT
#define
VEOL2
c_cc的另一个功能就是设置超时参数,MIN和TIME存储在c_cc数组中,通过VMIN和VTIME引用.
.MIN和TIME只在非加工方式下才有实际意义,它们分别用来控制等待多少字节的输入数据到达以及等待多长时间,单位为0.1秒。超时设置在下列两种情况下会失效:
在加工方式:使用O_NUNBLOCK参数调用open和fcntl函数,使IO变为非阻塞
对于非加工输入方式,典型的设置如下:
;
;
下面是MIN和TIME的功能组合表:
MIN |
MIN |
|
TIME |
read立即返回[0,nbytes] |
当队列中有大于MIN的字节时,read返回[MIN,nbytes]否则read会一直阻塞 |
TIME |
TIME没溢出时,read返回[MIN,nbytes] TIME溢出时,read返回[1,MIN] 这的TIME是read被阻塞的时间 |
TIME没溢出时,read返回[MIN,nbytes] TIME溢出时,read返回[1,MIN] 这个TIME是队列里接收到的字节间的时间,所以调用者可能会被一直阻塞 |
三:示例程序
上面介绍了大部分的串口通信会用到的函数和数据结构,Linux上写串口通讯程序时可以选择
采用多进程,当然也可以使用Pthread的多线程,不过我的示例程序并没有使用这些,和windows
上的示例程序类似,我还是认为这样可以更清晰的展现如何操作串口。
/**************************************************************************************************
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
main.c
Develop
Team : ls
Programmer
: He YiJun (storysnail<at>gmail.com)
Program
comments : Ling Ying
License
: GPLv3
Last
Update : 2013-03-25
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
功能说明:
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
更
新:
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
已知问题:
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
**************************************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include"uart-linux.h"
/**************************************************************************************************
函数名称:ME_Init()
函数功能:设备初始化,其实这个函数只是聋子的耳朵--摆设
函数说明:无
入口参数:fd:串口设备文件描述符
,失败返回-1
调用实例:无
**************************************************************************************************/
int
ME_Init(int
fd)
{
unsigned
char
];
unsigned
char
];
ssize_t
rCount
=
;
ssize_t
wCount
=
;
while
)
{
);
);
rCount
=
Com_Read(fd,ReadBuffer,COM_MAX_BUFFER);
if(rCount
>
)
{
printf("Read
com: %s\n",ReadBuffer);
printf("Read
com char num: %d\n",(int)rCount);
if((strstr((char
*)ReadBuffer,"AT")
!=
NULL)
&&
(strstr((char
*)ReadBuffer,"OK")
!=
NULL))
{
break;
}
}
);
);
]
=
'A';
]
=
'T';
]
=
0x0d;
]
=
'\0';
wCount
=
Com_Write(fd,WriteBuffer,strlen((char
*)WriteBuffer));
if(wCount
>
)
{
printf("Wrote
com: %s\n",WriteBuffer);
printf("Wrote
com char num: %d\n",(int)wCount);
}
}
return
;
}
/**************************************************************************************************
函数名称:
main()
函数功能:main()
函数说明:main()
入口参数:无
出口参数:0
调用实例:无
**************************************************************************************************/
int
main()
{
unsigned
char
];
unsigned
char
];
char
];
ssize_t
rCount
=
;
ssize_t
wCount
=
;
int
fd
=
;
if((fd
=
Com_Open())
==
)
{
return
;
}
,
,
,
,
)
==
)
{
Com_Close(fd);
return
;
}
if(ME_Init(fd)
==
)
{
Com_Close(fd);
return
;
}
while
)
{
);
printf
("\nEnter
Command: ");
if
(!fgets
(cmd,
CMD_MAX_LEN,stdin))
{
perror
("fget
error");
);
}
/*
Get
rid of the new line at the end */
/*
Messages
use 8-bit characters */
]
=
'\0';
if
(strcmp
(cmd,
"$Quit")
==
)
break;
if
(strncmp
(cmd,
"block",sizeof("block"))
==
)
{
if(Com_SetIOBlockFlag(fd,BLOCK_IO)
!=
)
{
printf("Set
IO block flags success!\n");
}
else
{
printf("Set
IO block flags error!\n");
}
}
if
(strncmp
(cmd,
"nonblock",sizeof("nonblock"))
==
)
{
if(Com_SetIOBlockFlag(fd,NONBLOCK_IO)
!=
)
{
printf("Set
IO no block flags success!\n");
}
else
{
printf("Set
IO no block flags error!\n");
}
}
if
(strncmp
(cmd,
"read",sizeof("read"))
==
)
{
);
rCount
=
Com_Read(fd,ReadBuffer,COM_MAX_BUFFER);
if(rCount
>
)
{
printf("ReadBuffer:
%s\n",ReadBuffer);
printf("Read
com char num: %d\n",(int)rCount);
}
}
if
(strncmp
(cmd,
"write",sizeof("write"))
==
)
{
memset(WriteBuffer,'\0',COM_MAX_BUFFER);
]
=
'A';
]
=
'T';
]
=
0x0d;
]
=
'\0';
printf("WriteBuffer:
%s\n",WriteBuffer);
wCount
=
Com_Write(fd,WriteBuffer,strlen((char
*)WriteBuffer));
);
);
rCount
=
Com_Read(fd,ReadBuffer,COM_MAX_BUFFER);
if(rCount
>
)
{
printf("ReadBuffer:
%s\n",ReadBuffer);
printf("Read
com char num: %d\n",(int)rCount);
}
}
}
Com_Close(fd);
return
;
}
/**************************************************************************************************
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
uart-linux.h
Develop
Team : ls
Programmer
: He YiJun (storysnail<at>gmail.com)
Program
comments : Ling Ying
License
: GPLv3
Last
Update : 2013-03-25
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
功能说明:
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
更
新:
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
已知问题:
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
**************************************************************************************************/
#ifndef
__UART_LINUX_H__
#define
__UART_LINUX_H__
#define
UART_MIN(A,B)
((A)
<
(B)
?
(A):(B))
#define
COM_MAX_BUFFER
//串口数据缓存的最大字节数
#define
BLOCK_IO
#define
NONBLOCK_IO
extern
int
Com_Open(void);
extern
int
Com_Close(int
fd);
extern
int
Com_SetIOBlockFlag(int
fd,int
value);
extern
int
Com_GetInQueByteCount(int
fd,int
*ByteCount);
extern
int
Com_Setup(int
fd,unsigned
int
baud,
int
databit,
int
stopbit,
int
parity,
int
flow);
extern
int
Com_ChangeBaudrate(int
fd,
unsigned
int
baud);
extern
ssize_t
Com_Read(int
fd,
unsigned
char
*ReadBuffer,
ssize_t
ReadSize);
extern
ssize_t
Com_Write(int
fd,
unsigned
char
*WriteBuffer,
ssize_t
WriteSize);
#endif
/**************************************************************************************************
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
uart-linux.c
Develop
Team : ls
Programmer
: He YiJun (storysnail<at>gmail.com)
Program
comments : Ling Ying
License
: GPLv3
Last
Update : 2013-03-25
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
功能说明:
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
更
新:
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
已知问题:
*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
**************************************************************************************************/
#include
<termios.h>
#include
<unistd.h>
#include
<fcntl.h>
#include
<sys/ioctl.h>
#include
<sys/select.h>
#include
<stdio.h>
#include
<string.h>
#include
"uart-linux.h"
/**************************************************************************************************
函数名称:Com_Open()
函数功能:打开串口
函数说明:无
入口参数:无
出口参数:成功返回串口设备文件描述符,失败返回-1
调用实例:无
**************************************************************************************************/
int
Com_Open(void)
{
int
fd
=
;
fd
=
open("/dev/ttyUSB0",
O_RDWR
|
O_NOCTTY
|
O_NONBLOCK);
if(fd
==
)
{
perror("open_port:
Unable to open /dev/ttyUSB0");
}
if(Com_SetIOBlockFlag(fd,BLOCK_IO)
==
)
printf("IO
set error\n");
return
(fd);
}
/**************************************************************************************************
函数名称:Com_Close()
函数功能:关闭串口
函数说明:无
入口参数:fd:串口设备文件描述符
出口参数:无
调用实例:无
**************************************************************************************************/
int
Com_Close(int
fd)
{
if
(fd
<
)
return
;
if
(close(fd)
==
)
return
;
printf("close
uart\n\n");
return
;
}
/**************************************************************************************************
函数名称:Com_SetIOBlockFlag()
函数功能:设置IO为阻塞或非阻塞
函数说明:无
入口参数:fd:串口设备文件描述符
value:BLOCK_IO或NONBLOCK_IO
出口参数:失败返回-1,否则返回其它值
调用实例:无
**************************************************************************************************/
int
Com_SetIOBlockFlag(int
fd,int
value)
{
int
oldflags;
if
(fd
==
)
return
;
oldflags
=
);
if(oldflags
==
)
{
printf("get
IO flags error\n");
return
;
}
if(value
==
BLOCK_IO)
oldflags
&=
~O_NONBLOCK;
//设置成阻塞IO
else
oldflags
|=
O_NONBLOCK;
//设置成非阻塞IO
return
fcntl(fd,F_GETFL,oldflags);
}
/**************************************************************************************************
函数名称:Com_GetInBufSize()
函数功能:得到串口输入队列中的字节数
函数说明:无
入口参数:fd:串口设备文件描述符
InBufSize:串口输入队列中的字节数会保存在该指针所指向的内存
出口参数:失败返回-1,否则返回0
调用实例:无
**************************************************************************************************/
int
Com_GetInQueByteCount(int
fd,int
*ByteCount)
{
int
bytes
=
;
if
(fd
==
)
return
;
if(ioctl(fd,
FIONREAD,
&bytes)
!=
)
{
*ByteCount
=
bytes;
return
;
}
return
;
}
/**************************************************************************************************
函数名称:Com_Setup()
函数功能:串口设定函数
函数说明:无
入口参数:fd:串口设备文件描述符
baud:比特率
300、600、1200、2400、4800、9600、19200、38400、57600、115200
databit:一个字节的数据位个数
5、6、7、8
stopbit:停止位个数1、2
parity:奇偶校验
0:无奇偶效验
1:奇效验
2:偶效验
flow:硬件流控制
0:无流控、
1:软件流控
2:硬件流控
出口参数:失败返回-1,否则返回0
调用实例:无
**************************************************************************************************/
int
Com_Setup(int
fd,unsigned
int
baud,
int
databit,
int
stopbit,
int
parity,
int
flow)
{
struct
termios
options;
if
(fd
==
)
return
;
if(tcgetattr(fd,
&options)
==
)
return
;
switch
(baud)
{
//取得比特率
case
:
options.c_cflag
=
B300;
break;
case
:
options.c_cflag
=
B600;
break;
case
:
options.c_cflag
=
B1200;
break;
case
:
options.c_cflag
=
B2400;
break;
case
:
options.c_cflag
=
B4800;
break;
case
:
options.c_cflag
=
B9600;
break;
case
:
options.c_cflag
=
B19200;
break;
case
:
options.c_cflag
=
B38400;
break;
case
:
options.c_cflag
=
B57600;
break;
case
:
options.c_cflag
=
B115200;
break;
default:
options.c_cflag
=
B19200;
break;
}
switch
(databit)
{
//取得一个字节的数据位个数
case
:
options.c_cflag
&=
~CSIZE;
options.c_cflag
|=
CS5;
break;
case
:
options.c_cflag
&=
~CSIZE;
options.c_cflag
|=
CS6;
break;
case
:
options.c_cflag
&=
~CSIZE;
options.c_cflag
|=
CS7;
break;
case
:
options.c_cflag
&=
~CSIZE;
options.c_cflag
|=
CS8;
break;
default:
options.c_cflag
&=
~CSIZE;
options.c_cflag
|=
CS8;
break;
}
switch
(parity)
{
//取得奇偶校验
case
:
options.c_cflag
&=
~PARENB;
//
无奇偶效验
options.c_iflag
&=
~(INPCK
|
ISTRIP);
//
禁用输入奇偶效验
options.c_iflag
|=
IGNPAR;
//
忽略奇偶效验错误
break;
case
:
options.c_cflag
|=
(PARENB
|
PARODD);
//
启用奇偶效验且设置为奇效验
options.c_iflag
|=
(INPCK
|
ISTRIP);
//
启用奇偶效验检查并从接收字符串中脱去奇偶校验位
options.c_iflag
&=
~IGNPAR;
//
不忽略奇偶效验错误
break;
case
:
options.c_cflag
|=
PARENB;
//
启用奇偶效验
options.c_cflag
&=
~PARODD;
//
设置为偶效验
options.c_iflag
|=
(INPCK
|
ISTRIP);
//
启用奇偶效验检查并从接收字符串中脱去奇偶校验位
options.c_iflag
&=
~IGNPAR;
//
不忽略奇偶效验错误
break;
default:
options.c_cflag
&=
~PARENB;
//
无奇偶效验
options.c_iflag
&=
~(INPCK
|
ISTRIP);
//
禁用输入奇偶效验
options.c_iflag
|=
IGNPAR;
//
忽略奇偶效验错误
break;
}
switch
(stopbit)
{
//取得停止位个数
case
:
options.c_cflag
&=
~CSTOPB;
//
一个停止位
break;
case
:
options.c_cflag
|=
CSTOPB;
//
2个停止位
break;
default:
options.c_cflag
&=
~CSTOPB;
//
默认一个停止位
break;
}
switch
(flow)
{
//取得流控制
case
:
options.c_cflag
&=
~CRTSCTS;
//
停用硬件流控制
options.c_iflag
&=
~(IXON
|
IXOFF
|
IXANY);
//
停用软件流控制
options.c_cflag
|=
CLOCAL;
//
不使用流控制
case
:
options.c_cflag
&=
~CRTSCTS;
//
停用硬件流控制
options.c_cflag
&=
~CLOCAL;
//
使用流控制
options.c_iflag
|=
(IXON
|
IXOFF
|
IXANY);
//
使用软件流控制
break;
case
:
options.c_cflag
&=
~CLOCAL;
//
使用流控制
options.c_iflag
&=
~(IXON
|
IXOFF
|
IXANY);
//
停用软件流控制
options.c_cflag
|=
CRTSCTS;
//
使用硬件流控制
break;
default:
options.c_cflag
&=
~CRTSCTS;
//
停用硬件流控制
options.c_iflag
&=
~(IXON
|
IXOFF
|
IXANY);
//
停用软件流控制
options.c_cflag
|=
CLOCAL;
//
不使用流控制
break;
}
options.c_cflag
|=
CREAD;
//
启用接收器
options.c_iflag
|=
IGNBRK;
//
忽略输入行的终止条件
options.c_oflag
=
;
//
非加工方式输出
options.c_lflag
=
;
//
非加工方式
//options.c_lflag
&= ~(ICANON | ECHO | ECHOE | ISIG);
//options.c_oflag
&= ~OPOST;
//如果串口输入队列没有数据,程序将在read调用处阻塞
options.c_cc[VMIN]
=
;
options.c_cc[VTIME]
=
;
if(tcsetattr(fd,
TCSANOW,
&options)
==
)
//
保存配置并立刻生效
return
;
//清空串口输入输出队列
tcflush(fd,
TCOFLUSH);
tcflush(fd,
TCIFLUSH);
return
;
}
/**************************************************************************************************
函数名称:Com_ChangeBaudrate()
函数功能:设定串口波特率
函数说明:无
入口参数:fd:串口设备文件描述符
baud:比特率
300、600、1200、2400、4800、9600、19200、38400、57600、115200
出口参数:成功返回0,失败返回-1
调用实例:无
**************************************************************************************************/
int
Com_ChangeBaudrate(int
fd,
unsigned
int
baud)
{
struct
termios
options;
struct
termios
old_options;
unsigned
int
baudrate
=
B19200;
if
(fd
==
)
return
;
if(tcgetattr(fd,
&old_options)
==
)
return
;
if(tcgetattr(fd,
&options)
==
)
return
;
switch
(baud)
{
case
:
baudrate
=
B300;
break;
case
:
baudrate
=
B600;
break;
case
:
baudrate
=
B1200;
break;
case
:
baudrate
=
B2400;
break;
case
:
baudrate
=
B4800;
break;
case
:
baudrate
=
B9600;
break;
case
:
baudrate
=
B19200;
break;
case
:
baudrate
=
B38400;
break;
case
:
baudrate
=
B57600;
break;
case
:
baudrate
=
B115200;
break;
default:
baudrate
=
B19200;
break;
}
if(cfsetispeed(&options,
baudrate)
==
)
return
;
if(cfsetospeed(&options,
baudrate)
==
)
{
tcsetattr(fd,
TCSANOW,
&old_options);
return
;
}
while(tcdrain(fd)
==
);
//tcdrain(fd);
// 保证输出队列中的所有数据都被传送
//清空串口输入输出队列
tcflush(fd,
TCOFLUSH);
tcflush(fd,
TCIFLUSH);
if(tcsetattr(fd,
TCSANOW,
&options)
==
)
{
tcsetattr(fd,
TCSANOW,
&old_options);
return
;
}
return
;
}
/**************************************************************************************************
函数名称:Com_Read()
函数功能:接收数据
函数说明:无
入口参数:fd:串口设备文件描述符
ReadBuffer:将数据写入ReadBuffer所指向的缓存区,并返回实际读到的字节数
ReadSize:欲读取的字节数
出口参数:成功返回实际读到的字节数,失败返回-1
调用实例:无
**************************************************************************************************/
ssize_t
Com_Read(int
fd,
unsigned
char
*ReadBuffer,
ssize_t
ReadSize)
{
ssize_t
rCount
=
;
//实际读到的字节数
ssize_t
dwBytesRead
=
;
int
InQueByteCount
=
;
if
(fd
<
)
{
perror("file
description is valid");
return
;
}
if
(ReadBuffer
==
NULL)
{
perror("read
buf is NULL");
return
;
}
if(ReadSize
>
COM_MAX_BUFFER)
dwBytesRead
=
COM_MAX_BUFFER;
else
dwBytesRead
=
ReadSize;
memset(ReadBuffer,
'\0',
dwBytesRead);
if(Com_GetInQueByteCount(fd,&InQueByteCount)
!=
)
{
printf("Uart
Queue have %d bytes\n",InQueByteCount);
dwBytesRead=UART_MIN(dwBytesRead,InQueByteCount);
}
if(!dwBytesRead)
return
;
rCount
=
read(fd,
ReadBuffer,
dwBytesRead);
if
(rCount
<
)
{
perror("read
error\n");
return
;
}
return
rCount;
}
/**************************************************************************************************
函数名称:Com_Write()
函数功能:发送数据
函数说明:无
入口参数:fd:串口设备文件描述符
WriteBuffer:将WriteBuffer所指向的缓冲区中的数据写入串口
WriteSize:欲写入的字节数
出口参数:成功返回实际写入的字节数,失败返回-1
调用实例:无
**************************************************************************************************/
ssize_t
Com_Write(int
fd,
unsigned
char
*WriteBuffer,
ssize_t
WriteSize)
{
ssize_t
wCount
=
;
//实际写入的字节数
ssize_t
dwBytesWrite=WriteSize;
if
(fd
<
)
{
perror("file
description is valid");
return
;
}
if((dwBytesWrite
>
COM_MAX_BUFFER)
||
(!dwBytesWrite))
return
;
wCount
=
write(fd,
WriteBuffer,
dwBytesWrite);
if
(wCount
<
)
{
perror("write
errot\n");
return
;
}
while(tcdrain(fd)
==
);
return
wCount;
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
#
makefile
#
#
Develop
Team
:
ls
#
Programmer
:
He
YiJun
(storysnail<at>gmail.com)
#
Program
comments
:
Ling
Ying
#
License
:
GPLv3
#
Last
Update
:
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
all:hara-CtlPanel-linux
CC=gcc
CFLAGS
=
-Wall
-ggdb
STD99
=
-std=c99
OBJFILES
=
main.o
uart-linux.o
hara-CtlPanel-linux:$(OBJFILES)
$(CC)
$(CFLAGS)
$(STD99)
$(OBJFILES)
-o
hara-CtlPanel-linux
main.o:uart-linux.h
$(CC)
$(CFLAGS)
-c
main.c
-o
main.o
uart.o:uart-linux.h
$(CC)
$(CFLAGS)
-c
uart-linux.c
-o
uart-linux.o
clean:
rm
-f
*.o
*~
hara-CtlPanel-linux
本文主要参考了
W.Richard
stevens所著的《Advanced
Programming in the UNIX Environment》
宋宝华编著的《linux设备驱动开发详解》
同时还参考了http://www.easysw.com/~mike/serial/serial.html的文章
storysnail的Linux串口编程笔记的更多相关文章
- storysnail的Windows串口编程笔记
storysnail的Windows串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据 ...
- Linux串口编程详解(转)
串口本身,标准和硬件 † 串口是计算机上的串行通讯的物理接口.计算机历史上,串口曾经被广泛用于连接计算机和终端设备和各种外部设备.虽然以太网接口和USB接口也是以一个串行流进行数据传送的,但是串口连接 ...
- linux串口编程总结
串口本身.标准和硬件 † 串口是计算机上的串行通讯的物理接口.计算机历史上,串口以前被广泛用于连接计算机和终端设备和各种外部设备.尽管以太网接口和USB接口也是以一个串行流进行数据传送的.可是串口连接 ...
- linux串口编程参数配置详解(转)
1.linux串口编程需要的头文件 #include <stdio.h> //标准输入输出定义#include <stdlib.h> //标准函数 ...
- linux串口编程参数配置详解
1.linux串口编程需要的头文件 #include <stdio.h> //标准输入输出定义 #include <stdlib.h> //标准函 ...
- linux串口编程
按照对linux系统的理解,串口编程的顺序无非就是open,read,write,close,而串口有波特率.数据位等重要参数需要设置,因此还应该用到设置函数,那么接下来就带着这几个问题去学习linu ...
- Linux串口编程进阶
在<Linux串口编程>编程一文中介绍了串口应用中常用的基本操作,如:串口打开关闭.串口设置.数据收发等.本篇文章主要基于常规串口操作进行了扩充,主要介绍如下操作: Linux系统使用非标 ...
- Linux串口编程(转载)
在嵌入式Linux中,串口是一个字设备,访问具体的串行端口的编程与读/写文件 的操作类似,只需打开相应的设备文件即可操作.串口编程特殊在于串 口通信时相关参数与属性的设置.嵌入式Linux的串口编程时 ...
- linux串口编程设置(转载)
(转载)在嵌入式Linux中,串口是一个字设备,访问具体的串行端口的编程与读/写文件 的操作类似,只需打开相应的设备文件即可操作.串口编程特殊在于串 口通信时相关参数与属性的设置.嵌入式Linux的串 ...
随机推荐
- ueditor不自动加P解决方法
百度的Ueditor编辑器出于安全考虑; 用户在html模式下粘贴进去的html文档会自动被去除样式和转义. 虽然安全的,但是非常不方便. 做一下修改把这个功能去掉. 一.打开ueditor.all. ...
- 转:HIBERNATE一些_方法_@注解_代码示例---写的非常好
HIBERNATE一些_方法_@注解_代码示例操作数据库7步骤 : 1 创建一个SessionFactory对象 2 创建Session对象 3 开启事务Transaction : hibernate ...
- 数据导出Excel
package com.jy.pjbj.web.action.util; import java.io.File;import java.util.List; import javax.xml.rpc ...
- Zabbix(一)--zabbix 2.4.8 安装
zabbix依赖于LAMP,所以部署前要先保证这个平台. 安装服务端(Server) zabbix官网的rpm包都是按照功能分开一个个,比如: zabbix-server-2.4.7-1.el7.x8 ...
- aa2
option = { series : [ { name: 'Map', type: 'map', mapLocation: { x : 'left', y : 'top', height : 500 ...
- android源码环境下用mmm/mm编译模块,输出编译log到文件的方法
android源码环境下用mmm/mm编译模块,输出编译log到文件的方法 1,在android目录下直接用mmm命令编译, log信息保存在android目录下 mmm packages/apps/ ...
- 关于CSS中text-decoration值没有替换而是累积的疑问
做了个实验: <!DOCTYPE html> <html> <head> <title>BaiDuTest.html</title> < ...
- 阿里云 centos 安装apache和php
mysql使用阿里云的rds httpd服务 1. 安装apr和apr-util 2. 安装 httpd apache.org,apr.apache.org 安装命令: ./configure --p ...
- eclipse生成uml
安装eclipse插件在help->Install new software里面add 有本地和网络两种 1.ModelGoon 该插件需要在file中new ModelGoon Diagram ...
- node学习笔记(四)
//Node.js标准库提供了http模块,其中封装了一个高效的http服务器和一个简易的http客户端 //http.Server是一个基于事件的HTTP服务器,它的核心由Node.js下层c++部 ...