MT7688学习笔记(6)——OpenWrt下串口编程

一、头文件

操作串口需要包含的头文件:

#include     /*标准输入输出定义*/
#include     /*错误号定义*/
#include 
#include     /*文件控制定义*/
#include   /*POSIX 终端控制定义*/
#include    /*标准函数库定义*/
#include 
#include    /*UNIX 标准函数定义*/

二、打开串口

我们都知道,在Linux下,除了网络设备,其余的都是文件的形式。串口设备也一样在/dev下。

所以我们可以通过open系统调用函数来访问它。

C 语言

#define serial_device "/dev/ttyS1"
//打开串口
int open_port(void)
{
	int fd;		//串口的标识符
	
	fd=open(serial_device,O_RDWR | O_NOCTTY | O_NDELAY);
	if(fd == -1)
	{
		//不能打开串口
		perror("open_port: Unable to open /dev/ttyS0 -");
		return(fd);
	}
	else
	{
		fcntl(fd, F_SETFL, 0);
		printf("open ttys1 .....\n");
		return(fd);
	}
}

//O_NOCTTY用来告诉Linux这个程序不会成为“控制终端”,不说明这个标志的话,任何输入都会影响你的程序。
//O_NDELAY用来告诉Linux这个程序不关心DCD信号,不说明这个标志的话,该程序就会在DCD信号线为低电平时停止。

C++
DALBase.h

class CDALBase
{
public:
	CDALBase();
	CDALBase(string strPath);
	virtual ~CDALBase(void);
	
	virtual int open(void);
	virtual int open(string strPath);
	virtual int close(void);

	virtual int read(unsigned char *pBuf, unsigned int iLen);
	virtual int write(string str);
	virtual int write(unsigned char *pBuf, unsigned int iLen);

	virtual int getFd(void) const;	
	virtual string getPath(void) const;

protected:
	virtual void setFd(int iFd);
	virtual void setPath(string strPath);
	
private:
		int m_iFd;
		string m_strPath;
};

DALBase.cpp

CDALBase::CDALBase()
{
}
CDALBase::CDALBase(string strPath):m_strPath(strPath)
{
}

CDALBase::~CDALBase()
{
	this->close();
}

int CDALBase::open(void)
{
	this->m_iFd  = ::open(this->m_strPath.c_str(), O_RDWR | O_NOCTTY);
	if(this->m_iFd <= 0)
	{
		cout << "CDALBase:" << this->m_strPath << "open fail!" << endl;
	}
	return this->m_iFd;
}

int CDALBase::open(string strPath)
{
	this->m_strPath = strPath;
	return this->open();
}
int CDALBase::close(void)
{
	if(m_iFd > 0)
	{
		return ::close(this->m_iFd);
	}
	return m_iFd;
}

int CDALBase::read(unsigned char *pBuf, unsigned int iLen)
{
	return ::read(this->m_iFd, pBuf, iLen);
}

int CDALBase::write(string str)
{
	return ::write(this->m_iFd, str.c_str(), str.length());
}
int CDALBase::write(unsigned char *pBuf, unsigned int iLen)
{	
	return ::write(this->m_iFd, pBuf, iLen);
}

int CDALBase::getFd(void) const
{
	return this->m_iFd;
}

string CDALBase::getPath(void) const
{
	return this->m_strPath;
}

void CDALBase::setFd(int iFd)
{
	this->m_iFd = iFd;
}
void CDALBase::setPath(string strPath)
{
	this->m_strPath = strPath;
}

Serial.h

class CSerialPort: public CDALBase
{
public:
	CSerialPort(string strPath = "/dev/ttyS1");
	~CSerialPort();
	
	int setParm(int baudrate = 115200, int databits = 8, int stopbits = 1, char parity = 'N');
	int getBaudRate(void);
	int getDatabits(void);
	int getStopbits(void);
	char getParity(void);
private:
	int m_iBaudRate;
	int m_iDatabits;
	int m_iStopbits;
	char m_cParity;
};

DALReceive.cpp

void *DALReceiveThread(void *arg)
{
	······
	int nFdSerialPort;							// 串口描述符
	CSerialPort serialPort;
	g_pDALSerialPort = &serialPort;
	serialPort.open();
    ······

三、读写串口

与普通文件一样,使用read,write函数。

C 语言

/**
  *串口发送数据函数
  *fd:串口描述符
  *data:待发送数据
  *datalen:数据长度
  */
int serial_write(int fd ,char *data, int datalen)
{
	int len=0;
	//获取实际传输数据的长度
	len=write(fd,data,datalen);
	printf("send data OK! datalen=%d\n",len);
	return len;	
}

/** 
  *串口接收数据 
  *要求启动后,在pc端发送ascii文件 
  */ 
int serial_read(int fd,char buff[],int datalen)
{
	int nread=0;
	printf("Ready for receiving data...");
	nread=read(fd,buff,datalen);
	if(nread>0)
	{
		printf("readlength=%d\n",nread);
		buff[nread]='\0';
		printf("%s\n",buff);
	}
	return nread;
}

C++
如第二部分代码

四、串口属性设置

最基本的设置串口包括 波特率 设置 校验位停止位 设置。这由通信双方协定。

很多系统都支持POSIX终端(串口)接口.程序可以利用这个接口来改变终端的参数,比如,波特率,字符大小等等.要使用这个端口的话,你必须将头文件包含到你的程序中。这个头文件中定义了终端控制结构体和POSIX控制函数。

最重要的就是这个结构体:

struct termios
{
    tcflag_t  c_iflag;    //输入模式标志,控制终端输入方式
    tcflag_t  c_oflag;    //输出模式标志,控制终端输出方式
    tcflag_t  c_cflag;    //控制模式标志,指定终端硬件控制信息
    tcflag_t  c_lflag;    //本地模式标志,控制终端编辑功能
    cc_t      c_cc[NCCS]; //控制字符,用于保存终端驱动程序中的特殊字符,如输入结束符等。
}; 

其中我们更关注的是c_cflag控制选项。其中包含了波特率、数据位、校验位、停止位的设置。
它可以支持很多常量名称其中设置数据传输率为相应的数据传输率前要加上“B”。
c_cflag成员不能直接对其初始化,而要将其通过与、或操作使用其中的某些选项。
设置串口属性主要是配置termios结构体中的各个变量,大致流程如下:

4.1 保存原串口属性

使用函数tcgetattr保存原串口属性

struct termios newtio,oldtio; 
tcgetattr(fd,&oldtio);

4.2 激活本地连接和接收使能

通过位掩码的方式激活本地连接和接收使能选项:CLOCAL和CREAD

newtio.c_cflag | = CLOCAL | CREAD;

4.3 设置波特率

使用函数cfsetispeed和cfsetospeed设置数据传输率

cfsetispeed(&newtio,B115200); 
cfsetospeed(&newtio,B115200);

4.4 设置数据位

通过位掩码设置字符大小和数据位

newtio.c_cflag &= ~CSIZE;//字符长度,设置数据位之前一定要屏掉这个位

newtio.c_cflag |= CS8;

4.5 设置奇偶校验位

设置奇偶校验位需要用到两个termios中的成员:c_cflag和c_iflag。首先要激活c_cflag中的校验位使能标志PARENB和是否进行奇偶校验,同时还要激活c_iflag中的奇偶校验使能。

//设置奇校验:
newtio.c_cflag |= PARENB; 
newtio.c_cflag |= PARODD; 
newtio.c_iflag |= (INPCK | ISTRIP); 
//设置偶校验:
newtio.c_cflag |= PARENB; 
newtio.c_cflag &= ~PARODD; 
newtio.c_iflag |= (INPCK | ISTRIP); 

4.6 设置停止位

激活c_cflag中的CSTOPB设置停止位。若停止位为1,则清除CSTOPB;若停止位为0,则激活CSTOPB

newtio.c_cflag &= ~CSTOPB;

4.7 设置数据流控制

数据流控制指是使用何种方法来标志数据传输的开始和结束。可以选择不使用数据流控制、使用硬件进行流控制和使用软件进行流控制

// 不使用数据流控制
opt.c_cflag &= ~CRTSCTS
// 硬件流控制
opt.c_cflag |= CRTSCTS
// 软件流控制
opt.c_cflag | = IXON|IXOFF|IXANY

4.8 设置最少字符和等待时间

设置最少字符和等待时间。在对接收字符和等待时间没有特别要求的情况下,可以将其设置为0

newtio.c_cc[VTIME] = 0; 
newtio.c_cc[VMIN] = 0;

4.9 清空终端未完成的输入/输出请求及数据

调用函数”tcflush(fd,queue_selector)”来处理要写入引用的对象,queue_selector可能的取值有以下几种。
TCIFLUSH:刷新收到的数据但是不读
TCOFLUSH:刷新写入的数据但是不传送
TCIOFLUSH:同时刷新收到的数据但是不读,并且刷新写入的数据但是不传送

4.10 激活配置

激活配置。在完成配置后,需要激活配置使其生效。使用tcsetattr()函数

int tcsetattr(int filedes,int opt,const struct termios *termptr);

五、关闭串口

串口作为文件来处理,所以一般的关闭文件函数即可:
close(fd);

六、串口设置代码示例

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


int set_serial(int fd,int nSpeed,int nBits,char nEvent,int nStop)
{
    struct termios newttys1,oldttys1;

     /*保存原有串口配置*/
     if(tcgetattr(fd,&oldttys1)!=0) 
     {
          perror("Setupserial 1");
          return -1;
     }
     bzero(&newttys1,sizeof(newttys1));
     newttys1.c_cflag|=(CLOCAL|CREAD ); /*CREAD 开启串行数据接收,保证程序可以从串口中读取数据
                                      ;CLOCAL打开本地连接模式,保证程序不占用串口*/

     newttys1.c_cflag &=~CSIZE;//字符长度,设置数据位之前一定要屏掉这个位
     /*数据位选择*/   
     switch(nBits)
     {
         case 7:
             newttys1.c_cflag |=CS7;
             break;
         case 8:
             newttys1.c_cflag |=CS8;
             break;
     }
     /*设置奇偶校验位*/
     switch( nEvent )
     {
         case '0':  /*奇校验*/
             newttys1.c_cflag |= PARENB;/*开启奇偶校验*/
             newttys1.c_iflag |= (INPCK | ISTRIP);/*INPCK打开输入奇偶校验;ISTRIP去除字符的第八个比特  */
             newttys1.c_cflag |= PARODD;/*启用奇校验(默认为偶校验)*/
             break;
         case 'E':/*偶校验*/
             newttys1.c_cflag |= PARENB; /*开启奇偶校验  */
             newttys1.c_iflag |= ( INPCK | ISTRIP);/*打开输入奇偶校验并去除字符第八个比特*/
             newttys1.c_cflag &= ~PARODD;/*启用偶校验*/
             break;
         case 'N': /*无奇偶校验*/
             newttys1.c_cflag &= ~PARENB;
             break;
     }
     /*设置波特率*/
    switch( nSpeed )  
    {
        case 2400:
            cfsetispeed(&newttys1, B2400);
            cfsetospeed(&newttys1, B2400);
            break;
        case 4800:
            cfsetispeed(&newttys1, B4800);
            cfsetospeed(&newttys1, B4800);
            break;
        case 9600:
            cfsetispeed(&newttys1, B9600);
            cfsetospeed(&newttys1, B9600);
            break;
        case 115200:
            cfsetispeed(&newttys1, B115200);
            cfsetospeed(&newttys1, B115200);
            break;
        default:
            cfsetispeed(&newttys1, B9600);
            cfsetospeed(&newttys1, B9600);
            break;
    }
     /*设置停止位*/
    if( nStop == 1)/*设置停止位;若停止位为1,则清除CSTOPB,若停止位为2,则激活CSTOPB*/
    {
        newttys1.c_cflag &= ~CSTOPB;/*默认为一位停止位; */
    }
    else if( nStop == 2)
    {
        newttys1.c_cflag |= CSTOPB;/*CSTOPB表示送两位停止位*/
    }

    /*--------------------其他配置-----------------------*/
    /*设置本地模式为原始模式*/
	Opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    /*
		 *ICANON:允许规范模式进行输入处理
		 *ECHO:允许输入字符的本地回显
		 *ECHOE:在接收EPASE时执行Backspace,Space,Backspace组合
		 *ISIG:允许信号
	*/
	/*设置输出模式为原始输出*/
	Opt.c_oflag &= ~OPOST;  //OPOST:若设置则按定义的输出处理,否则所有c_oflag失效
    /*发送字符0X0d的时候,往往接收端得到的字符是0X0a,原因是因为在串口设置中c_iflag和c_oflag中存在从NL-CR和CR-NL的映射,即串口能把回车和换行当成同一个字符,可以进行如下设置屏蔽之*/
	Opt.c_oflag &= ~(ONLCR | OCRNL);
	Opt.c_iflag &= ~(ICRNL | INLCR);	
	Opt.c_cflag &= ~CRTSCTS;// 不使用数据流控制
    Opt.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); /*c_cc数组的VSTART和VSTOP元素被设定成DC1和DC3,代表ASCII标准的XON和XOFF字符,如果在传输这两个字符的时候就传不过去,需要把软件流控制屏蔽*/

    /*设置最少字符和等待时间,对于接收字符和等待时间没有特别的要求时*/
    newttys1.c_cc[VTIME] = 0;/*非规范模式读取时的超时时间;*/
    newttys1.c_cc[VMIN]  = 0; /*非规范模式读取时的最小字符数*/
    tcflush(fd ,TCIFLUSH);/*tcflush清空终端未完成的输入/输出请求及数据;TCIFLUSH表示清空正收到的数据,且不读取出来 */

     /*激活配置使其生效*/
    if((tcsetattr( fd, TCSANOW,&newttys1))!=0)
    {
        perror("com set error");
        return -1;
    }

    return 0;
}

七、附录

表1 c_iflag参数表

键 值 说 明
IGNBRK 忽略BREAK键输入
BRKINT 如果设置了IGNBRK,BREAK键输入将被忽略
IGNPAR 忽略奇偶校验错误
PARMRK 标识奇偶校验错误
INPCK 允许输入奇偶校验
ISTRIP 去除字符的第8个比特
INLCR 将输入的NL(换行)转换成CR(回车)
IGNCR 忽略输入的回车
ICRNL 将输入的回车转化成换行(如果IGNCR未设置的情况下)
IUCLC 将输入的大写字符转换成小写字符(非POSIX)
IXON 允许输出时对XON/XOFF流进行控制
IXANY 输入任何字符将重启停止的输出
IXOFF 允许输入时对XON/XOFF流进行控制
IMAXBEL 当输入队列满的时候开始响铃

表2 c_oflag参数

键 值 说 明
OPOST 处理后输出
OLCUC 将输出的小写字符转换成大写字符(非POSIX)
ONLCR 将输出的NL(换行)转换成CR(回车)及NL(换行)
OCRNL 将输出的CR(回车)转换成NL(换行)
ONOCR 第一行不输出回车符
ONLRET 不输出回车符
OFILL 发送填充字符以延迟终端输出
OFDEL 以ASCII码的DEL作为填充字符,如果未设置该参数,填充字符为NUL
NLDLY 换行输出延时,可以取NL0(不延迟)或NL1(延迟0.1s)
CRDLY 回车延迟,取值范围为:CR0、CR1、CR2和 CR3
TABDLY 水平制表符输出延迟,取值范围为:TAB0、TAB1、TAB2和TAB3
BSDLY 空格输出延迟,可以取BS0或BS1
VTDLY 垂直制表符输出延迟,可以取VT0或VT1
FFDLY 换页延迟,可以取FF0或FF1

表3 c_cflag参数

键 值 说 明
CBAUD 波特率(4+1位)(非POSIX)
CBAUDEX 附加波特率(1位)(非POSIX)
CSIZE 字符长度,取值范围为CS5、CS6、CS7或CS8
CSTOPB 设置两个停止位
CREAD 使用接收器
PARENB 使用奇偶校验
PARODD 对输入使用奇偶校验,对输出使用偶校验
HUPCL 关闭设备时挂起
CLOCAL 忽略调制解调器线路状态
CRTSCTS 使用RTS/CTS流控制

表4 c_lflag参数

键 值 说 明
ISIG 当输入INTR、QUIT、SUSP或DSUSP时,产生相应的信号
ICANON 使用标准输入模式
XCASE 在ICANON和XCASE同时设置的情况下,终端只使用大写
ECHO 显示输入字符
ECHOE 如果ICANON同时设置,ERASE将删除输入的字符
ECHOK 如果ICANON同时设置,KILL将删除当前行
ECHONL 如果ICANON同时设置,即使ECHO没有设置依然显示换行符
ECHOPRT 如果ECHO和ICANON同时设置,将删除打印出的字符(非POSIX)
TOSTOP 向后台输出发送SIGTTOU信号

表5 c_cc支持的控制字符

说 明
VINTR Interrupt字符
VQUIT Quit字符
VERASE Erase字符
VKILL Kill字符
VEOF End-of-file字符
VMIN 非规范模式读取时的最小字符数
VEOL 附加的End-of-file字符
VTIME 非规范模式读取时的超时时间
VSTOP Stop字符
VSTART Start字符
VSUSP Suspend字符

• 由 Leung 写于 2018 年 10 月 9 日

• 参考:C——Linux下的串口编程
    Linux 下串口编程之四 编程实现
    linux下串口通讯参数设置

你可能感兴趣的:(MT7688)