写这篇文章的原因:因为在linux开发串口应用的时候,遇到了问题,让遇到相同问题的人少走点弯路:
①读串口数据的时,需要接受换行符才能返回。
②接受数据时,一个字节一个字节的返回。无法接受完多个字节在返回。
对于 linux的开发板来说,串口的驱动是不需要我们去开发,我目前是在内核4.9上开发,只需要修改一下设备树就可以了。所以直接对设备文件进访问就可以了。
linux使用串口的方法:
1.串口配置的头文件:
#include
2.串口波特率的设置:
波特率 | |
B4800 | 4800 |
B9600 | 9600 |
B67500 | 67500 |
B115200 | 115200 |
修改波特率是使用cfsetispeed()和cfsetospeed()函数来操作。
例如:要将波特率修改为115200:cfsetispeed(&opts, B115200);
cfsetospeed(&opts,B115200);
3.串口属性配置:
串口的属性定义在结构体struct termios,其头文件#include
#define NCCS 19
struct termios {
tcflag_t c_iflag; /* 输入参数 */
tcflag_t c_oflag; /* 输出参数 */
tcflag_t c_cflag; /* 控制参数*/
tcflag_t c_lflag; /* 本地模式 */
tcflag_t c_ispeed; /* 输入波特率 */
tcflag_t c_ospeed; /* 输出波特率 */
cc_t c_line; /* 线控制 */
cc_t c_cc[NCCS]; /* 控制字符*/
};
对串口属性的获取和设置有两个:
① tcsetattr(int fd, int opt_DE, termios *ptr) ,返回值:成功返回0,失败返回-1。
fd:文件描述符
opt_DE:选项值,可供选择:
(1)TCSANOW: 不等数据传输完毕就立即改变属性
(2)TCSADRAIN:等待所有数据传输结束才改变属性
(3)TCSAFLUSH:清空输入输出缓冲区才改变属性
*ptr:指向termios结构的指针
②tcgetsttr(int fd, termios *ptr),返回值:成功返回0,失败返回-1。
fd:待操作的文件描述符
*ptr:指向termios结构的指针
4.属性描述:
①:c_iflag:
IGNBRK:忽略输入中的 BREAK 状态。
BRKINT:如果设置了 IGNBRK,将忽略 BREAK。如果没有设置,但是设置了 BRKINT,那么 BREAK 将使得输入和输出队列被刷新,如果终端是一个前台进程组的控制终端,这个进程组中所有进程将收到 SIGINT 信号。如果既未设置IGNBRK 也未设置 BRKINT,BREAK 将视为与 NUL 字符同义,除非设置了 PARMRK,这种情况下它被视为序列 /377 /0 /0。
IGNPAR:忽略桢错误和奇偶校验错。
PARMRK:如果没有设置 IGNPAR,在有奇偶校验错或桢错误的字符前插入 /377 /0。如果既没有设置 IGNPAR 也没有设置PARMRK,将有奇偶校验错或桢错误的字符视为 /0。
INPCK:启用输入奇偶检测。
ISTRIP:去掉第八位。
INLCR:将输入中的 NL 翻译为 CR。
IGNCR:忽略输入中的回车。
ICRNL:将输入中的回车翻译为新行 (除非设置了 IGNCR)。
IUCLC:(不属于 POSIX) 将输入中的大写字母映射为小写字母。
IXON:启用输出的 XON/XOFF 流控制。
IXANY:(不属于 POSIX.1;XSI) 允许任何字符来重新开始输出。(?)
IXOFF:启用输入的 XON/XOFF 流控制。
IMAXBEL:(不属于 POSIX) 当输入队列满时响零。Linux 没有实现这一位,总是将它视为已设置。
②c_oflag:
OPOST:启用具体实现自行定义的输出处理。其余 c_oflag 标志常量定义在 POSIX 1003.1-2001 中,除非另外说明。
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:
CBAUD:(不属于 POSIX) 波特率掩码 (4+1 位)。
CBAUDEX:(不属于 POSIX) 扩展的波特率掩码 (1 位),包含在 CBAUD 中。
(POSIX 规定波特率存储在 termios 结构中,并未精确指定它的位置,而是提供了函数 cfgetispeed() 和 cfsetispeed() 来存取它。一些系统使用 c_cflag 中 CBAUD 选择的位,其他系统使用单独的变量,例如 sg_ispeed 和 sg_ospeed 。)
CSIZE:字符长度掩码。取值为 CS5, CS6, CS7, 或 CS8。
CSTOPB:设置两个停止位,而不是一个。
CREAD:打开接受者。
PARENB:允许输出产生奇偶信息以及输入的奇偶校验。
PARODD:输入和输出是奇校验。
HUPCL:在最后一个进程关闭设备后,降低 modem 控制线 (挂断)。(?)
CLOCAL:忽略 modem 控制线。
LOBLK:(不属于 POSIX) 从非当前 shell 层阻塞输出(用于 shl )。(?)
CIBAUD:(不属于 POSIX) 输入速度的掩码。CIBAUD 各位的值与 CBAUD 各位相同,左移了 IBSHIFT 位。
CRTSCTS:(不属于 POSIX) 启用 RTS/CTS (硬件) 流控制。
④c_lflag:
ISIG:当接受到字符 INTR, QUIT, SUSP, 或 DSUSP 时,产生相应的信号。
ICANON:启用标准模式 (canonical mode)。允许使用特殊字符 EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, 和 WERASE,以及按行的缓冲。
XCASE:(不属于 POSIX; Linux 下不被支持) 如果同时设置了 ICANON,终端只有大写。输入被转换为小写,除了以 / 前缀的字符。输出时,大写字符被前缀 /,小写字符被转换成大写。
ECHO:回显输入字符。
ECHOE:如果同时设置了 ICANON,字符 ERASE 擦除前一个输入字符,WERASE 擦除前一个词。
ECHOK:如果同时设置了 ICANON,字符 KILL 删除当前行。
ECHONL:如果同时设置了 ICANON,回显字符 NL,即使没有设置 ECHO。
ECHOCTL:(不属于 POSIX) 如果同时设置了 ECHO,除了 TAB, NL, START, 和 STOP 之外的 ASCII 控制信号被回显为 ^X, 这里 X 是比控制信号大 0x40 的 ASCII 码。例如,字符 0x08 (BS) 被回显为 ^H。
ECHOPRT:(不属于 POSIX) 如果同时设置了 ICANON 和 IECHO,字符在删除的同时被打印。
ECHOKE:(不属于 POSIX) 如果同时设置了 ICANON,回显 KILL 时将删除一行中的每个字符,如同指定了 ECHOE 和ECHOPRT 一样。
DEFECHO:(不属于 POSIX) 只在一个进程读的时候回显。
FLUSHO:(不属于 POSIX; Linux 下不被支持) 输出被刷新。这个标志可以通过键入字符 DISCARD 来开关。
NOFLSH:禁止在产生 SIGINT, SIGQUIT 和 SIGSUSP 信号时刷新输入和输出队列。
TOSTOP:向试图写控制终端的后台进程组发送 SIGTTOU 信号。
PENDIN:(不属于 POSIX; Linux 下不被支持) 在读入下一个字符时,输入队列中所有字符被重新输出。(bash 用它来处理 typeahead)
IEXTEN:启用实现自定义的输入处理。这个标志必须与 ICANON 同时使用,才能解释特殊字符 EOL2,LNEXT,REPRINT 和 WERASE,IUCLC 标志才有效。
⑤c_cc[],数组定义了特殊的控制字符。符号下标 (初始值) 和意义为:
VINTR:(003, ETX, Ctrl-C, or also 0177, DEL, rubout) 中断字符。发出 SIGINT 信号。当设置 ISIG 时可被识别,不再作为输入传递。
VQUIT:(034, FS, Ctrl-/) 退出字符。发出 SIGQUIT 信号。当设置 ISIG 时可被识别,不再作为输入传递。
VERASE:(0177, DEL, rubout, or 010, BS, Ctrl-H, or also #) 删除字符。删除上一个还没有删掉的字符,但不删除上一个 EOF 或行首。当设置 ICANON 时可被识别,不再作为输入传递。
VKILL:(025, NAK, Ctrl-U, or Ctrl-X, or also @) 终止字符。删除自上一个 EOF 或行首以来的输入。当设置 ICANON 时可被识别,不再作为输入传递。
VEOF:(004, EOT, Ctrl-D) 文件尾字符。更精确地说,这个字符使得 tty 缓冲中的内容被送到等待输入的用户程序中,而不必等到 EOL。如果它是一行的第一个字符,那么用户程序的 read() 将返回 0,指示读到了 EOF。当设置 ICANON 时可被识别,不再作为输入传递。
VMIN:非 canonical 模式读的最小字符数。
VEOL:(0, NUL) 附加的行尾字符。当设置 ICANON 时可被识别。
VTIME:非 canonical 模式读时的延时,以十分之一秒为单位。
VEOL2:(not in POSIX; 0, NUL) 另一个行尾字符。当设置 ICANON 时可被识别。
VSWTCH:(not in POSIX; not supported under Linux; 0, NUL) 开关字符。(只为 shl 所用。)
VSTART:(021, DC1, Ctrl-Q) 开始字符。重新开始被 Stop 字符中止的输出。当设置 IXON 时可被识别,不再作为输入传递。
VSTOP:(023, DC3, Ctrl-S) 停止字符。停止输出,直到键入 Start 字符。当设置 IXON 时可被识别,不再作为输入传递。
VSUSP:(032, SUB, Ctrl-Z) 挂起字符。发送 SIGTSTP 信号。当设置 ISIG 时可被识别,不再作为输入传递。
VDSUSP:(not in POSIX; not supported under Linux; 031, EM, Ctrl-Y) 延时挂起信号。当用户程序读到这个字符时,发送 SIGTSTP 信号。当设置 IEXTEN 和 ISIG,并且系统支持作业管理时可被识别,不再作为输入传递。
VLNEXT:(not in POSIX; 026, SYN, Ctrl-V) 字面上的下一个。引用下一个输入字符,取消它的任何特殊含义。当设置 IEXTEN 时可被识别,不再作为输入传递。
VWERASE:(not in POSIX; 027, ETB, Ctrl-W) 删除词。当设置 ICANON 和 IEXTEN 时可被识别,不再作为输入传递。
VREPRINT:(not in POSIX; 022, DC2, Ctrl-R) 重新输出未读的字符。当设置 ICANON 和 IEXTEN 时可被识别,不再作为输入传递。
VDISCARD:(not in POSIX; not supported under Linux; 017, SI, Ctrl-O) 开关:开始/结束丢弃未完成的输出。当设置 IEXTEN 时可被识别,不再作为输入传递。
VSTATUS:(not in POSIX; not supported under Linux; status request: 024, DC4, Ctrl-T).
⑥:VIME 和VMIN需要配合使用,关系如下:
1、VTIME=0,VMIN=0:此时即使读取不到任何数据,函数read也会返回,返回值是0。
2、VTIME=0,VMIN>0:read调用一直阻塞,直到读到VMIN个字符后立即返回。
3、VTIME>0,VMIN=0:read调用读到数据则立即返回,否则将为每个字符最多等待 VTIME*100ms 时间。
4、VTIME>0,VMIN>0:read调用将保持阻塞直到读取到第一个字符,读到了第一个字符之后开始计时,此后若时间到了 VTIME*100ms 或者时间未到但已读够了VMIN个字符则会返回。若在时间未到之前又读到了一个字符(但此时读到的总数仍不够VMIN)则计时重新开始(即每个字符都有VTIME*100ms的超时时间)。
①设置波特率:
void set_baudrate(struct termios *set_serial, unsigned long int baud_rate)
{
int baud = B115200;
switch(baud_rate)
{
case 2400:
baud = B2400;
break;
case 4800:
baud = B4800;
break;
case 9600:
baud = B9600;
break;
case 19200:
baud = B19200;
break;
case 38400:
baud = B38400;
break;
case 57600:
baud = B57600;
break;
case 115200:
baud = B115200;
break;
default:
baud = B115200;
break;
}
cfsetispeed(set_serial, baud);
cfsetospeed(set_serial, baud);
}
②设置数据位:
void set_databits(struct termios *set_serial, unsigned int data_bits)
{
switch(data_bits)
{
case 5:
set_serial->c_cflag |= CS5;
break;
case 6:
set_serial->c_cflag |= CS6;
break;
case 7:
set_serial->c_cflag |= CS7;
break;
case 8:
set_serial->c_cflag |= CS8;
break;
default:
set_serial->c_cflag |= CS8;
break;
}
}
③设置校验位:
void set_parity(struct termios *set_serial, char parity)
{
switch(parity)
{
case 'N':
set_serial->c_cflag &= ~PARENB; //no parity check
break;
case 'O':
set_serial->c_cflag |= PARENB; //odd check
set_serial->c_cflag &= ~PARODD;
break;
case 'E':
set_serial->c_cflag |= PARENB; //even check
set_serial->c_cflag |= PARODD;
break;
default:
set_serial->c_cflag &= ~PARENB;
break;
}
}
④停止位:
void set_stopbits(struct termios *set_serial, unsigned int stop_bits)
{
if(stop_bits == 2)
{
set_serial->c_cflag |= CSTOPB; //2 stop bits
}
else
{
set_serial->c_cflag &= ~CSTOPB; //1 stop bits
}
}
⑤串口配置函数:
void set_option(unsigned int baud_rate, unsigned int data_bits, char parity, unsigned int stop_bits)
{
struct termios opts;
tcgetattr(m_dev, &opts);
set_baudrate(&opts, baud_rate);
opts.c_cflag |= CLOCAL|CREAD;
set_parity(&opts, parity);
set_stopbits(&opts, stop_bits);
set_databits(&opts, data_bits);
opts.c_cflag &= ~CRTSCTS;
opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //raw input
opts.c_oflag &= ~OPOST; // raw output
opts.c_cc[VTIME]=1;
opts.c_cc[VMIN]=1023;
tcsetattr(m_dev, TCSANOW, &opts);
}
⑥发送数据:
int send_data(int fd, const char *data, int datalen)
{
int len = 0;
len = write(m_dev, data, datalen);
if(len == datalen)
{
return 0;
}
else
{
tcflush(m_dev, TCOFLUSH);
return -1;
}
}
7接收函数:
int receive(int fd, char *data, int datalen)
{
int read_len;
if((read_len = read(m_dev, data, datalen))>0)
{
return read_len;
}
else
{
return -1;
}
}
⑧主函数:
int main( int argc, char *argv[])
{
int fd;
int ret;
char buff[1024];
char senddata[] = "uart";
fd= open("/dev/ttyS2", O_RDWR | O_NOCTTY | O_NONBLOCK);
if(fd <= 0)
{
printf("uart open fail\n");
return -1;
}
fcntl(fd, F_SETFL, 0);
set_option(115200, 8, 'N', 1);
while (1)
{
ret = receive(fd, buff, sizeof(buff));
buff[ret] = 0;
printf("receive data : %s\n", buff);
send_data(fd, senddata, sizeof(senddata));
}
close(fd);
return 0;
}
完整代码:https://download.csdn.net/download/jiafanluo/11231218