linux串口应用编程入门,文档胜于一切教程

接触过linux编程的应该都知道,学习linux应用最好的参考资料就是系统自带的手册——通过man命令查找编程手册。

通过搜索引擎搜索与linux串口编程相关的关键字,找到与串口编程相关的结构体或者函数就可以开始自学串口应用编程了。

学习环境:deepin 15.3 64位系统,开发板:nanopc-t1,运行ubuntu 14.04LTS,内核版本:3.8.13.16,交叉编译链:arm-linux-gnueabihf-gcc,版本4.7.3

我是从串口相关的termios结构体开始入手的,终端中输入man 3 termios

hokamyuen@hokamyuen-deepin:~$ man 3 termios

就能打开串口编程手册了,学习过程中遇到的串口相关的问题都可以在这里找到答案。

打开手册,可以看到串口相关的许多函数,而且很多函数都含有struct termios这个结构体,所以这个结构体一定是重点,继续往下翻手册可以看到这个结构体的介绍

DESCRIPTION
       The termios functions describe a general terminal interface that is provided to control asynchronous  communi‐
       cations ports.
   The termios structure
       Many of the functions described here have a termios_p argument that is a pointer to a termios structure.  This
       structure contains at least the following members:

           tcflag_t c_iflag;      /* input modes */
           tcflag_t c_oflag;      /* output modes */
           tcflag_t c_cflag;      /* control modes */
           tcflag_t c_lflag;      /* local modes */
           cc_t     c_cc[NCCS];   /* special characters */

描述中说到,termios这个结构体是用于描述一个串口的各个参数的,结构体包含输入,输出模式等参数。不过,这些参数跟学单片机时配置的参数完全不一样,而且最重要的一点貌似没有——这个结构体描述的是哪个串口。继续翻手册,又发现了一个读取设置和生效配置的说明

Retrieving and changing terminal settings
       tcgetattr() gets the parameters associated with the object referred by fd  and  stores  them  in  the  termios
       structure  referenced by termios_p.  This function may be invoked from a background process; however, the ter‐
       minal attributes may be subsequently changed by a foreground process.

       tcsetattr() sets the parameters associated with the terminal (unless support is required from  the  underlying
       hardware  that is not available) from the termios structure referred to by termios_p.  optional_actions speci‐
       fies when the changes take effect:
这样的话我们只需要修改跟我们预期的配置不一样的地方然后再生效配置就好了。那首先我们就要开始使用这个函数看看串口原本的参数了。

回到手册的头部

SYNOPSIS
       #include <termios.h>
       #include <unistd.h>

       int tcgetattr(int fd, struct termios *termios_p);

可以看到所有的函数生明和需要的头文件,首先把手册列出来的头文件添加到我们的代码中

#include <termios.h>
#include <unistd.h>

int main(void)
{
    return 0;
}

再看tcgetattr函数的第一个入口参数为fd,很明显这个为文件描述符,不难想到我们要控制的是哪个串口就是由这个参数来决定的。文件描述符在打开文件的时候生成,所以我们需要打开串口设备,同样利用man 命令查找open函数的使用方法和需要的头文件

hokamyuen@hokamyuen-deepin:~$ man 3 open
SYNOPSIS
       #include <sys/stat.h>
       #include <fcntl.h>


       int open(const char *path, int oflag, ...);
       int openat(int fd, const char *path, int oflag, ...);
可以看到open函数需要sys/stat.h,fcntl.h两个头文件,open函数的第一个入口参数为文件的路径,至于第二个参数oflag我们可以继续翻手册寻找可以选用的参数
Values for oflag are constructed by a bitwise-inclusive OR of  flags  from  the  following  list,  defined  in
       <fcntl.h>.   Applications  shall specify exactly one of the first five values (file access modes) below in the
       value of oflag:

       O_EXEC        Open for execute only (non-directory files). The result is unspecified if this flag  is  applied
                     to a directory.

       O_RDONLY      Open for reading only.

       O_RDWR        Open for reading and writing. The result is undefined if this flag is applied to a FIFO.

       O_SEARCH      Open  directory  for  search  only.  The result is unspecified if this flag is applied to a non-
                     directory file.

       O_WRONLY      Open for writing only.

       Any combination of the following may be used:

       O_APPEND      If set, the file offset shall be set to the end of the file prior to each write.

       O_CLOEXEC     If set, the FD_CLOEXEC flag for the new file descriptor shall be set.

       O_CREAT       If the file exists, this flag has no effect except as noted under O_EXCL below.  Otherwise,  the
                     file  shall  be  created;  the  user ID of the file shall be set to the effective user ID of the
                     process; the group ID of the file shall be set to the group ID of the file's parent directory or
                     to  the  effective group ID of the process; and the access permission bits (see <sys/stat.h>) of
                     the file mode shall be set to the value of the argument following the oflag  argument  taken  as
                     type mode_t modified as follows: a bitwise AND is performed on the file-mode bits and the corre‐
                     sponding bits in the complement of the process' file mode creation mask. Thus, all bits  in  the
                     file  mode  whose corresponding bit in the file mode creation mask is set are cleared. When bits
                     other than the file permission bits are set, the effect is unspecified. The  argument  following
                     the  oflag  argument does not affect whether the file is open for reading, writing, or for both.
                     Implementations shall provide a way to initialize the file's group ID to the  group  ID  of  the
                     parent  directory.  Implementations  may, but need not, provide an implementation-defined way to
                     initialize the file's group ID to the effective group ID of the calling process.

       O_DIRECTORY   If path resolves to a non-directory file, fail and set errno to [ENOTDIR].

       O_DSYNC       Write I/O operations on the file descriptor shall complete as defined by synchronized  I/O  data
                     integrity completion.

       O_EXCL        If O_CREAT and O_EXCL are set, open() shall fail if the file exists. The check for the existence
                     of the file and the creation of the file if it does not exist shall be atomic  with  respect  to
                     other  threads  executing  open() naming the same filename in the same directory with O_EXCL and
                     O_CREAT set. If O_EXCL and O_CREAT are set, and path names a symbolic link,  open()  shall  fail
                     and set errno to [EEXIST], regardless of the contents of the symbolic link. If O_EXCL is set and
                     O_CREAT is not set, the result is undefined.

       O_NOCTTY      If set and path identifies a terminal device, open() shall not  cause  the  terminal  device  to
                     become  the  controlling  terminal for the process. If path does not identify a terminal device,
                     O_NOCTTY shall be ignored.

       O_NOFOLLOW    If path names a symbolic link, fail and set errno to [ELOOP].

       O_NONBLOCK    When opening a FIFO with O_RDONLY or O_WRONLY set:

                      *  If O_NONBLOCK is set, an open() for reading-only shall return without delay. An  open()  for
                         writing-only shall return an error if no process currently has the file open for reading.

                      *  If  O_NONBLOCK  is  clear, an open() for reading-only shall block the calling thread until a
                         thread opens the file for writing. An open() for writing-only shall block the calling thread
                         until a thread opens the file for reading.

                     When opening a block special or character special file that supports non-blocking opens:

                      *  If O_NONBLOCK is set, the open() function shall return without blocking for the device to be
                         ready or available. Subsequent behavior of the device is device-specific.

                      *  If O_NONBLOCK is clear, the open() function shall block the calling thread until the  device
                         is ready or available before returning.

                     Otherwise,  the O_NONBLOCK flag shall not cause an error, but it is unspecified whether the file
                     status flags will include the O_NONBLOCK flag.
O_RSYNC       Read I/O operations on the file descriptor shall complete at the  same  level  of  integrity  as
                     specified by the O_DSYNC and O_SYNC flags. If both O_DSYNC and O_RSYNC are set in oflag, all I/O
                     operations on the file descriptor shall complete as defined by synchronized I/O  data  integrity
                     completion. If both O_SYNC and O_RSYNC are set in flags, all I/O operations on the file descrip‐
                     tor shall complete as defined by synchronized I/O file integrity completion.

       O_SYNC        Write I/O operations on the file descriptor shall complete as defined by synchronized  I/O  file
                     integrity completion.

                     The  O_SYNC flag shall be supported for regular files, even if the Synchronized Input and Output
                     option is not supported.

       O_TRUNC       If the file exists and is a regular  file,  and  the  file  is  successfully  opened  O_RDWR  or
                     O_WRONLY,  its  length  shall  be  truncated to 0, and the mode and owner shall be unchanged. It
                     shall have no effect on FIFO special files or terminal device files. Its effect  on  other  file
                     types  is  implementation-defined. The result of using O_TRUNC without either O_RDWR or O_WRONLY
                     is undefined.

       O_TTY_INIT    If path identifies a terminal device other than a pseudo-terminal, the  device  is  not  already
                     open  in  any  process,  and either O_TTY_INIT is set in oflag or O_TTY_INIT has the value zero,
                     open() shall set any non-standard termios structure terminal parameters to a state that provides
                     conforming  behavior;  see the Base Definitions volume of POSIX.1‐2008, Section 11.2, Parameters
                     that Can be Set.  It is unspecified whether O_TTY_INIT has any effect if the device  is  already
                     open  in any process. If path identifies the slave side of a pseudo-terminal that is not already
                     open in any process, open() shall set any non-standard termios structure terminal parameters  to
                     a state that provides conforming behavior, regardless of whether O_TTY_INIT is set. If path does
                     not identify a terminal device, O_TTY_INIT shall be ignored.
这里我选择了读写权限标志O_RDWR和非控制终端标志O_NOCTTY两个选项,打开文件这句代码的参数就找齐了,打开文件后要记得关闭

#include <termios.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(void)
{	
	int fd;
	char tty_addr[] = "/dev/ttySAC2";
	fd = open(tty_addr,O_RDWR|O_NOCTTY);
	if(fd < 0)
	{
		printf("打开%s失败\r\n",tty_addr);
		return 0;
	}
	printf("打开%s成功\r\n",tty_addr);
	close(fd);
	return 0;
}

每做完一步先编译并且放上开发板测试一遍,我用的是nanopc-t1

hokamyuen@hokamyuen-deepin:~/nanopc/usart-test$ arm-linux-gnueabihf-gcc usart-test.c -o usart-test -static
没有错误提示而且生成了usart-test文件就证明编译成功了。用自己决觉得方便的方法吧程序下载的板子里,我比较喜欢用scp方式。运行程序并观察输出结果

root@NanoPC:~# ./usart-test 
打开/dev/ttySAC2成功
root@NanoPC:~# 
可以看到,程序顺利的运行了,接着进行下一步——获取串口的参数。我们首先看看串口的波特率。

#include <termios.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(void)
{	
	int fd,uart_in_speed,uart_out_speed;
	char tty_addr[] = "/dev/ttySAC2";
	struct termios uart_setting_struct;
	fd = open(tty_addr,O_RDWR|O_NOCTTY);
	if(fd < 0)
	{
		printf("打开%s失败\r\n",tty_addr);
		return 0;
	}
	printf("打开%s成功\r\n",tty_addr);
	tcgetattr(fd,&uart_setting_struct);
	uart_in_speed = cfgetispeed(&uart_setting_struct);
	uart_out_speed = cfgetospeed(&uart_setting_struct);
	printf("串口输入速度:%d,输出速度:%d\r\n",uart_in_speed,uart_out_speed);
	close(fd);
	return 0;
}
编译并下载到板子上运行

root@NanoPC:~# ./usart-test 
打开/dev/ttySAC2成功
串口输入速度:13,输出速度:13
root@NanoPC:~# 
运行之后可以看到串口的输入和输出的速度都不是常见的波特率,有疑惑就要重新去翻看手册寻找答案。

Line speed
       The baud rate functions are provided for getting and setting the values of the input and output baud rates  in
       the termios structure.  The new values do not take effect until tcsetattr() is successfully called.

       Setting  the speed to B0 instructs the modem to "hang up".  The actual bit rate corresponding to B38400 may be
       altered with setserial(8).

       The input and output baud rates are stored in the termios structure.

       cfgetospeed() returns the output baud rate stored in the termios structure pointed to by termios_p.

       cfsetospeed() sets the output baud rate stored in the termios structure pointed  to  by  termios_p  to  speed,
       which must be one of these constants:

            B0
            B50
            B75
            B110
            B134
            B150
            B200
            B300
            B600
            B1200
            B1800
            B2400
            B4800
            B9600
            B19200
            B38400
            B57600
            B115200
            B230400

       The  zero  baud  rate,  B0,  is used to terminate the connection.  If B0 is specified, the modem control lines
       shall no longer be asserted.  Normally, this will disconnect the line.  CBAUDEX  is  a  mask  for  the  speeds
       beyond those defined in POSIX.1 (57600 and above).  Thus, B57600 & CBAUDEX is nonzero.

       cfgetispeed() returns the input baud rate stored in the termios structure.

       cfsetispeed()  sets  the  input baud rate stored in the termios structure to speed, which must be specified as
       one of the Bnnn constants listed above for cfsetospeed().  If the input baud rate is set to  zero,  the  input
       baud rate will be equal to the output baud rate.

       cfsetspeed()  is  a  4.4BSD  extension.  It takes the same arguments as cfsetispeed(), and sets both input and
       output speed.

RETURN VALUE
       cfgetispeed() returns the input baud rate stored in the termios structure.

       cfgetospeed() returns the output baud rate stored in the termios structure.

       All other functions return:

       0      on success.

       -1     on failure and set errno to indicate the error.

       Note that tcsetattr() returns success if any of the requested  changes  could  be  successfully  carried  out.
       Therefore,  when  making multiple changes it may be necessary to follow this call with a further call to tcge‐
       tattr() to check that all changes have been performed successfully.
通过手册,我们发现串口的波特率不是直接输入数字设置的,而是通过一组宏,刚刚获取到的速度为13,即指B9600宏,表示的波特率为9600,那下一步就可以尝试修改串口的波特率了,修改设置后记得使设置生效。

Retrieving and changing terminal settings
       tcgetattr() gets the parameters associated with the object referred by fd  and  stores  them  in  the  termios
       structure  referenced by termios_p.  This function may be invoked from a background process; however, the ter‐
       minal attributes may be subsequently changed by a foreground process.

       tcsetattr() sets the parameters associated with the terminal (unless support is required from  the  underlying
       hardware  that is not available) from the termios structure referred to by termios_p.  optional_actions speci‐
       fies when the changes take effect:

       TCSANOW
              the change occurs immediately.

       TCSADRAIN
              the change occurs after all output written to fd has been transmitted.  This option should be used when
              changing parameters that affect output.

       TCSAFLUSH
              the  change  occurs after all output written to the object referred by fd has been transmitted, and all
              input that has been received but not read will be discarded before the change is made.
在tcsetattr函数的说明后面有几个生效设置选项,通过各个宏的说明,可以看到这些宏代表设置生效的时间,立刻生效,发送数据完成后生效,发送数据完成后生效而且接收到的而且没被程序读取的数据将被丢弃。按需选择就好,在当前的简单应用中,可以选择任意一个,我选择了立刻生效。源码如下:

#include <termios.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

#define UART_BAUD B115200

int main(void)
{	
	int fd,uart_in_speed,uart_out_speed;
	char tty_addr[] = "/dev/ttySAC2";
	char send_buf[] = "hello";
	struct termios uart_setting_struct;
	fd = open(tty_addr,O_RDWR|O_NOCTTY);
	if(fd < 0)
	{
		printf("打开%s失败\r\n",tty_addr);
		return 0;
	}
	printf("打开%s成功\r\n",tty_addr);
	tcgetattr(fd,&uart_setting_struct);
	uart_in_speed = cfgetispeed(&uart_setting_struct);
	if(uart_in_speed != UART_BAUD)
	{
		cfsetispeed(&uart_setting_struct,UART_BAUD);
	}
	uart_out_speed = cfgetospeed(&uart_setting_struct);
	if(uart_out_speed != UART_BAUD)
	{
		cfsetospeed(&uart_setting_struct,UART_BAUD);
	}
	tcsetattr(fd,TCSANOW,&uart_setting_struct);
	write(fd,send_buf,strlen(send_buf));
	close(fd);
	return 0;
}
以115200的波特率打开串口调试助手,我用的是picocom,-b表示使用的波特率,/dev/ttyUSB0为usb转串口模块的串口号,ctrl+a,ctrl+q可以退出调试助手

hokamyuen@hokamyuen-deepin:~$ sudo picocom -b 115200 /dev/ttyUSB0
打开调试助手之后,把编译号好的程序下载的板子上并运行,可以在调试助手中看到输出的信息

picocom v1.7

port is        : /dev/ttyUSB0
flowcontrol    : none
baudrate is    : 115200
parity is      : none
databits are   : 8
escape is      : C-a
local echo is  : no
noinit is      : no
noreset is     : no
nolock is      : no
send_cmd is    : sz -vv
receive_cmd is : rz -vv
imap is        : 
omap is        : 
emap is        : crcrlf,delbs,

Terminal ready
hello
Thanks for using picocom
串口设置好之后就当做普通文件来读写就好了。

此文章为本人的学习过程,如有错误请指出!
















  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值