Linux串口编程老生常谈了,已经做过很多次,但有些细节没次都要翻遍百度才能找到答案,这里做笔记记录下
1.首先先包含一些头文件,这里只做应用的方法介绍,具体头文件怎么回事,自个百度
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <errno.h>
#include <string.h>
#include <termio.h>
2.打开串口,在Linux下打开设备和打开文件一样
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
fd问串口的文件句柄,为整型,open函数打开设备,第一个参数为需要打开的设备名,一般在PC下的Linux为"/dev/ttySx",再嵌入式ARM上是"/dev/ttySACx",
接着就是打开这个设备的参数。
首先是读写模式,有三种:O_RDONLY
终端模式:O_NOCTTY
一般串口都只是用于传输数据就可以;
阻塞模式:O_NONBLOCK 非阻塞模式,O_NDELAY阻塞模式
阻塞的定义:
对于read,当串口输入缓冲区没有数据的时候,read函数将会阻塞在这里,当串口输入缓冲区中有数据可读取,read读到了需要的字节数之后,返回值为读到的字节数;
对于write,当串口输出缓冲区满,或剩下的空间小于将要写入的字节数,则write将阻塞,一直到串口输出缓冲区中剩下的空间大于等于将要写入的字节数,执行写入操作,返回写入的字节数。
非阻塞的定义:
对于read,当串口输入缓冲区没有数据的时候,read函数立即返回,返回值为0。
对于write,当串口输出缓冲区满,或剩下的空间小于将要写入的字节数,则write将进行写操作,写入当前串口输出缓冲区剩下空间允许的字节数,然后返回写入的字节数。
3.设置串口参数
这里用到一个重要的结构体,串口的波特率、数据位、校验位、停止位的设置由这个结构体实现:
struct termios
{
tcflag_t c_iflag; //input flags
tcflag_t c_oflag; //output flags
tcflag_t c_cflag; //control flags
tcflag_t c_lflag; //local flags
cc_t c_cc[NCCS]; //control characters
};
该结构体中c_cflag最为重要,可设置波特率、数据位、校验位、停止位。在设置波特率时需要在数字前加上'B',
如B9600,B15200.使用其需通过“与”“或”操作方式:
定义这个结构体,并分配内存空间
struct termios serialAttr;
memset(&serialAttr, 0, sizeof serialAttr);
接下来设置具体参数
输入模式c_iflag成员控制端口接收端的字符输入处理:
我一般使用这种:serialAttr.c_iflag = IGNPAR;
就是忽略奇偶校验
接下来就是重点c_cflag的设置了
c_cflag一些常数(复制来的~)
常量 | 描述 |
CBAUD | Bit mask for baud rate |
B0 | 0 baud (drop DTR) |
B50 | 50 baud |
B75 | 75 baud |
B110 | 110 baud |
B134 | 134.5 baud |
B150 | 150 baud |
B200 | 200 baud |
B300 | 300 baud |
B600 | 600 baud |
B1200 | 1200 baud |
B1800 | 1800 baud |
B2400 | 2400 baud |
B4800 | 4800 baud |
B9600 | 9600 baud |
B19200 | 19200 baud |
B38400 | 38400 baud |
B57600 | 57,600 baud |
B76800 | 76,800 baud |
B115200 | 115,200 baud |
EXTA | External rate clock |
EXTB | External rate clock |
CSIZE | Bit mask for data bits |
CS5 | 5 data bits |
CS6 | 6 data bits |
CS7 | 7 data bits |
CS8 | 8 data bits |
CSTOPB | 2 stop bits (1 otherwise) |
CREAD | Enable receiver |
PARENB | Enable parity bit |
PARODD | Use odd parity instead of even |
HUPCL | Hangup (drop DTR) on last close |
CLOCAL | Local line - do not change "owner" of port |
LOBLK | Block job control output |
CNEW_RTSCTS/CRTSCTS | Enable hardware flow control (not supported on all platforms) |
一般的像下面都是这样设置就够了
serialAttr.c_cflag = B115200 | HUPCL | CS8 | CREAD | CLOCAL;
这里的意思是:设置波特率为115200,最后关闭时断开,发送8位的数据,启动接收装置,忽略解制;
字符数组c_cc里面包括了控制字符的定义和超时参数。这个数组的每个元素都是以常量定义的。
成员变量c_cc中的控制字符
常量 | 描述 | 键 |
VINTR | Interrupt | CTRL-C |
VQUIT | Quit | CTRL-Z |
VERASE | Erase | Backspace (BS) |
VKILL | Kill-line | CTRL-U |
VEOF | End-of-file | CTRL-D |
VEOL | End-of-line | Carriage return (CR) |
VEOL2 | Second end-of-line | Line feed (LF) |
VMIN | Minimum number of characters to read | - |
VSTART | Start flow | CTRL-Q (XON) |
VSTOP | Stop flow | CTRL-S (XOFF) |
VTIME | Time to wait for data (tenths of seconds) | - |
VMIN可以指定读取的最小字符数。如果它被设置为0,那么VTIME值则会指定每个字符读取的等待时间。
如果VMIN不为零,VTIME会指定等待第一个字符读取操作的时间。如果在这个指定时间中可以开始读取某个字符,直到VMIN个数的所有字符全部被读取,其他读取操作将会被阻塞(等待)。也就是说,一旦读取第一个字符,串口驱动的预期就是接收到整个字符包(一共VMIN字节)。如果在允许的时间内没有字符被读取,那么read(2)调用就会返回0。通过这个方法可以确切得告诉串口驱动程序需要读取N个字节,而且read(2)调用只会返回N或者0。然而,超时设置只对第一个字符的读取操作有效,所以,如果因为某些原因驱动程序在N字节的包中丢失某个字符的话,read(2)调用将会一直等下去。
VTIME可以以十分之一秒为单位指定等待字符输入的时间,即是毫秒~。如果VTIME设置为0(默认情况),除非open(2)或者fcntl(2)函数设置了NDELAY选项,否则read(2)将会永久得阻塞(等待)。
再啰嗦一点:
A.如果VTIME取0,VMIN定义了要求等待读取的最小字节数。函数read()只有在读取了VMIN个字节的数据或者收到一个信号的时候才返回。
B.如果VMIN取0,VTIME定义了即使没有数据可以读取,read()函数返回前也要等待几百毫秒的时间量。这时,read()函数不需要像其通常情况那样要遇到一个文件结束标志才返回0.
C.如果VTIME和VMIN都不取0,VTIME定义的是当接收到第一个字节的数据后开始计算等待的时间量。如果当调用read函数时可以得到数据,计时器马上开始计时。如果当调用read函数时还没有任何数据可读,则等接收到第一个字节的数据后,计时器开始计时。函数read可能会在读取到VMIN个字节的数据后返回,也可能在计时完毕后返回,这主要取决于哪个条件首先实现。不过函数至少会读取到一个字节的数据,因为计时器是在读取到第一个数据时开始计时的。
D.如果VTIME和VMIN都取0,即使读取不到任何数据,函数read也会立即返回。同时,返回值0表示read函数不需要等待文件结束标志就返回了。
还有一个函数设置阻塞
阻塞:fcntl(fd,F_SETFL,0)
非阻塞:fcntl(fd,F_SETFL,FNDELAY)
好像这种方法比open里更有效
设置完串口后下面就很简单了
4.往串口发送数据
ssize_t write(int fd, const void *buf, size_t count);
参数:
fd:要进行写操作的文件描述词,即串口句柄
buf:需要输出的缓冲区,即需要发送的字符串
count:最大输出字节计数,一般为需要发送的字符串长度
返回写入串口的长度
5.从串口中读数据
ssize_t read(int fd, void *buf, size_t count);
与读类似,fd,句柄,buf,用来装载接收的数据,count,需要接收的长度,返回接收到的值得长度