竞渡深悲千载冤,忠魂一去讵能还。国亡身殒今何有,只留离骚在世间。——张耒《和端午》
内核版本:3.0.54
串口通信是我们常见的一种通信,在很多开发中不可缺少,这篇文章主要讲如何设置我们的串口,以便后续开发。
前面介绍UART的时候已经概述,我们主要用到三根针来传输数据,即RXD,TXD,GND。具体的连接前面有说明,大家可前往查看。
串口的配置
1.波特率:为了两者信号流能同步,因此收发双方的波特率应该一致。
2.数据位:几位数据封装成一帧,因为数据以帧为单位传输,所以这里要设置。
3.结束位:数据在传输时需要判断什么时候结束,因此结束位能够保证接收方获得有效数据。
4.奇偶校验:检验数据的一种有效方法,需要板子和模块同时开启。
Linux中的串口参数在/dev下,我们板子的串口为/dev/ttyS0,/dev/ttyS1,这里出于不可抗拒因素,我们的这两个串口我都没有使用,我用的是USB接口来连接我们的串口,即ttyUSB0。
当然,要是用USB接口,那就得移植USB驱动咯,这里推荐一篇好的文章:http://blog.csdn.net/edroid1530/article/details/70186871
除了这篇文章上的东东,我们还需要在内核中添加我们的USB转串口的内容:
export TERM=vt100
make menuconfig
Device Drivers ——>
[*] USB support ——>
<*> Support for Host-side USB
[*] USB device filesystem (DEPRECATED)
[*] USB device class-devices (DEPRECATED)
<*> USB Monitor
<*> OXU210HP HCD support
<*> OHCI HCD support
<*> USB Mass Storage support
[*] The shared table of common (or usual) storage devices
<*> USB Serial Converter support --->
<*> USB Winchiphead CH341 Single Port Serial Driver
<*> USB CP210x family of UART Bridge Controllers
<*> USB FTDI Single Port Serial Driver
<*> USB Prolific 2303 Single Port Serial Driver
后面部分主要是对串口的设置。
在Linux下,一切皆文件,串口也不例外,我们对他的操作也是read、write、等等。
串口结构体:
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是最重要的,波特率、数据位、校验位以及停止位都在这里设置。
关于c_cflag:
CCTS_OFLOW | 输出的CTS流控制 |
---|---|
CIGNORE | 忽略控制标志 |
CLOCAL | 忽略解制-解调器状态行 |
CREAD | 启用接收装置 |
CRTS_IFLOW | 输入的RTS流控制 |
CSIZE | 字符大小屏蔽 |
CSTOPB | 送两个停止位,否则为1位 |
HUPCL | 最后关闭时断开 |
MDMBUF | 经载波的流控输出 |
PARENB | 进行奇偶校验 |
PARODD | 奇校验,否则为默认偶校验 |
关于c_iflag
BRKINT | 接到BREAK时产生SIGINT |
---|---|
ICRNL | 将输入的CR转换为NL |
IGNBRK | 忽略BREAK条件 |
IGNCR | 忽略CR |
IGNPAR | 忽略奇偶错字符 |
IMAXBEL | 在输入队列空时振铃 |
INLCR | 将输入的NL转换为CR |
INPCK | 打开输入奇偶检验 |
ISTRIP | 剥除输入字符的第8位 |
IUCLC | 将输入的大写字符转换成小写字符 |
IXANY | 使任一字符都重新启动输出 |
IXOFF | 使启动/停止输入控制流起作用 |
IXON | 使启动/停止输出控制流起作用 |
PARMRK | 标记奇偶错 |
串口的控制函数:
Tcgetattr | 取属性(termios结构) |
---|---|
Tcsetattr | 设置属性(termios结构) |
cfgetispeed | 得到输入速度 |
Cfgetospeed | 得到输出速度 |
Cfsetispeed | 设置输入速度 |
Cfsetospeed | 设置输出速度 |
Tcdrain | 等待所有输出都被传输 |
tcflow | 挂起传输或接收 |
tcflush | 刷清未决输入和/或输出 |
Tcsendbreak | 送BREAK字符 |
tcgetpgrp | 得到前台进程组ID |
tcsetpgrp | 设置前台进程组ID |
配置流程
1.保存原先串口配置,tcgetattr(fd,&oldtio)函数:
struct termios newtio,oldtio;
tcgetattr(fd,&oldtio);
2.激活选项:CLOCAL和CREAD,用于本地连接和接收使用
newtio.c_cflag | = CLOCAL | CREAD;
3.设置波特率:cfsetispeed、cfsetospeed,数字前面加个’B’。
cfsetispeed(&newtio,B115200);
cfsetospeed(&newtio,B115200);
4.设置数据位,需用掩码
newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS8;
5.设置奇偶校验位,使用c_cflag和c_iflag
奇校验:
newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
偶校验:
newtio.c_iflag |= (INPCK|ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag |= ~PARODD;
6.设置停止位,通过激活c_cflag中的CSTOPB实现。若停止位为1,则清除CSTOPB,若停止位为2,则激活CSTOPB。
newtio.c_cflag &= ~CSTOPB;
7.设置最少字符和等待时间,对于接收字符和等待时间没有特别的要求时,可设为0
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
8.处理要写入的引用对象
tcflush函数刷清(抛弃)输入缓存(终端驱动程序已接收到,但用户程序尚未读)或输出缓存(用户程序已经写,但尚未发送)
int tcflush(int filedes,int quene)
其中quene: *TCIFLUSH 刷清输入队列、*TCOFLUSH 刷清输出队列、 *TCIOFLUSH 刷清输入、输出队列。
ex:tcflush(fd,TCIFLUSH);
9.激活配置。在完成配置后,需要激活配置使其生效。使用tcsetattr()函数:
int tcsetattr(int filedes,int opt,const struct termios *termptr);
opt使我们可以指定在什么时候新的终端属性才起作用
*TCSANOW:更改立即发生
*TCSADRAIN:发送了所有输出后更改才发生。若更改输出参数则应使用此选项
*TCSAFLUSH:发送了所有输出后更改才发生。更进一步,在更改发生时未读的所有输入数据都被删除(刷清)
ex: tcsetattr(fd,TCSANOW,&newtio);
串口的使用
1.打开串口
fd = open(“/dev/ttyUSB0”, O_RDWR | O_NOCTTY | O_NDELAY);
参数:
O_NOCTTY:通知linux系统,这个程序不会成为这个端口的控制终端
O_NDELAY:通知linux系统不关心DCD信号线所处的状态(端口的另一端是否激活或者停止)
然后恢复串口的状态为阻塞状态,用于等待串口数据的读入,用fcntl函数:
fcntl(fd,F_SETFL,0); //F_SETFL:设置文件flag为0,即默认,即阻塞状态
接着测试打开的文件描述符是否应用一个终端设备,以进一步确认串口是否正确打开
isatty(STDIN_FILENO);
读写串口这里就不详细说了,就是操作文件一样:
read(fd,buff,8);
write(fd,buff,8);
下面给一个通用的设置串口的程序:
/*********************************************************************************
* Copyright: (C) 2017 tangyanjun<519656780@qq.com>
* All rights reserved.
*
* Filename: serial.c
* Description: This file
*
* Version: 1.0.0(05/28/2017)
* Author: tangyanjun <519656780@qq.com>
* ChangeLog: 1, Release initial version on "05/28/2017 11:12:13 AM"
*
********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
/**************************************************************************************
* Description: 串口参数配置
* Input Args: fd:open打开的文件描述符 nspeed:波特率 nBits:数据位数 nEvent:奇偶校验 nStop:停止位
* Output Argtingzhis: 串口参数设置失败返回-1
* Return Value:
*************************************************************************************/
int set_opt(int fd, int nSpeed, int nBits, char nEvent, int nStop)
{
struct termios newttyUSB0, oldttyUSB0;
if(tcgetattr(fd, &oldttyUSB0) != 0) //保存原先串口配置
{
perror("Setupserial 1");
return -1;
}
bzero(&newttyUSB0, sizeof(newttyUSB0)); //将一段内存区域的内容全清为零
newttyUSB0.c_cflag |= (CLOCAL | CREAD ); //CREAD 开启串行数据接收,CLOCAL并打开本地连接模式
newttys1.c_cflag &= ~CSIZE; //设置数据位数
switch(nBits) //选择数据位
{
case 7:
newttyUSB0.c_cflag |=CS7;
break;
case 8:
newttyUSB0.c_cflag |=CS8;
break;
}
switch( nEvent ) //设置校验位
{
case '0': //奇校验
newttyUSB0.c_cflag |= PARENB; //开启奇偶校验
newttyUSB0.c_iflag |= (INPCK | ISTRIP); //INPCK打开输入奇偶校验;ISTRIP去除字符的第八个比特
newttyUSB0.c_cflag |= PARODD; //启用奇校验(默认为偶校验)
break;
case 'E' : //偶校验
newttyUSB0.c_cflag |= PARENB; //开启奇偶校验
newttyUSB0.c_iflag |= ( INPCK | ISTRIP); //打开输入奇偶校验并去除字符第八个比特
newttyUSB0.c_cflag &= ~PARODD; //启用偶校验;
break;
case 'N': //关闭奇偶校验
newttyUSB0.c_cflag &= ~PARENB;
break;
}
switch( nSpeed ) //设置波特率
{
case 2400:
cfsetispeed(&newttyUSB0, B2400); //设置输入速度
cfsetospeed(&newttyUSB0, B2400); //设置输出速度
break;
case 4800:
cfsetispeed(&newttyUSB0, B4800);
cfsetospeed(&newttyUSB0, B4800);
break;
case 9600:
cfsetispeed(&newttyUSB0, B9600);
cfsetospeed(&newttyUSB0, B9600);
break;
case 115200:
cfsetispeed(&newttyUSB0, B115200);
cfsetospeed(&newttyUSB0, B115200);
break;
default:
cfsetispeed(&newttyUSB0, B9600);
cfsetospeed(&newttyUSB0, B9600);
break;
}
if( nStop == 1) //设置停止位;若停止位为1,则清除CSTOPB,若停止位为2,则激活CSTOPB。
{
newttyUSB0.c_cflag &= ~CSTOPB; //默认为送一位停止位;
}
else if( nStop == 2)
{
newttyUSB0.c_cflag |= CSTOPB; //CSTOPB表示送两位停止位;
}
//设置最少字符和等待时间,对于接收字符和等待时间没有特别的要求时
newttyUSB0.c_cc[VTIME] = 0; //非规范模式读取时的超时时间;
newttyUSB0.c_cc[VMIN] = 0; //非规范模式读取时的最小字符数;
tcflush(fd ,TCIFLUSH); //tcflush清空终端未完成的输入/输出请求及数据;TCIFLUSH表示清空正收到的数据,且不读取出来
// 在完成配置后,需要激活配置使其生效
if((tcsetattr( fd, TCSANOW, &newttyUSB0)) != 0) //TCSANOW不等数据传输完毕就立即改变属性
{
perror("com set error");
return -1;
}
return 0;
} /* ----- End of if() ----- */