itop4412之 linux串口编程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_39956356/article/details/86564646

目录

  • 基本知识
  • 结构体termio
  • 串口初始化步骤
(一)基本知识

linux下的串口通常指RS232,比51,或stm32都要复杂些,当然实质是一样的,比如都有涉及的波特率,停止位,数据位,奇偶校验等,对于驱动,每个板子都会有所不同,但是实质都一样的。这里是itop4412的exynos4412,如果你是其他开发板希望对你有帮助。
在这里插入图片描述

(二)结构体termio

linux下的串口第一个就是要学习termio结构体,很重要!
位置: 内核目录“\arch\arm\include\asm\termios.h”, 或者用
man 3 tcgetattr也可以看到。
这里是TERMIOS(3).pdf—参考文档
链接: https://pan.baidu.com/s/1783ZZK1ICWP490ZJ89SDFA 提取码: 5btc
在这里插入图片描述

(三)串口初始化步骤是
  • (1)读取当前参数
  • (2)修改参数
  • (3)配置参数

首先先看看几个常见的函数

(1):读取当前参数在这里插入图片描述
(2):读取当前波特率—有两个(有一个输入,一个输出)
在这里插入图片描述
(3):设置新波特率—有两个(有一个输入,一个输出)在这里插入图片描述
(4):清空串口BUFFER中的数据函数
在这里插入图片描述
(5):设置串口参数函数
在这里插入图片描述

之后看一个例程与分享自己学习所得

下面的参数是会遇到的,详细请参考man文档

在强调一遍,linux下的串口使用步骤:获取当前值----配置新结构体—生效

里面涉及两个termio结构体变量,一个新的,一个旧的。
在这里插入图片描述

串口配置函数

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

/* struct termio {
	unsigned short c_iflag;		// input mode flags 
	unsigned short c_oflag;		// output mode flags 
	unsigned short c_cflag;		// control mode flags 
		//CREAD: Enable receiver;	CLOCAL: Ignore modem control lines.
	unsigned short c_lflag;		// local mode flags 
	unsigned char c_line;		// line discipline 
	unsigned char c_cc[NCC];	// control characters
};  */
/****************************************************************************************
**									串口配置函数
**	tcgetattr():读取当前参数的函数
**		int tcgetattr(int fd, struct termios *termios_p);
**	bzero():置字节字符串前n个字节为零且包括‘\0’。
**		extern void bzero(void *s, int n)
**		s: 要置零的数据的起始地址
**		n: 要置零的数据字节个数。
**	tcflush():清空串口BUFFER中的数据函数
**		int tcflush(int fd, int queue_selector);
**		fd :是open返回的文件句柄。
**		queue_selector:控制tcflush 的操作。有三个常用数值,
			TCIFLUSH: 清除正收到的数据,且不会读取出来;
			TCOFLUSH: 清除正写入的数据,且不会发送至终端;
			TCIOFLUSH:清除所有正在发生的I/O 数据。
**		返回值:执行成功返回0,失败返回-1
**	tcsetattr():设置新的参数。一般在初始化最后会使用这个函数
**		int tcsetattr(int fd, int optional_actions,const struct termios *termios_p);
**		fd:open返回的文件句柄。
**		optional_actions:参数生效的时间。
			TCSANOW:  不等数据传输完毕就立即改变属性;
			TCSADRAIN:等待所有数据传输结束才改变属性;
			TCSAFLUSH:清空输入输出缓冲区才改变属性。
**		*termios_p :在旧的参数基础上修改的后的参数。
**		返回值:执行成功返回0,失败返回-1
****************************************************************************************/
int set_opt(int fd, int nSpeed, int nBits, char nEvent, int nStop)
{
	struct termios newtio, oldtio;						//新旧termios结构体变量
	if(tcgetattr(fd, &oldtio) != 0)						//读取当前参数
	{
		perror("SetupSerial 1");
		return -1;
	}
	
	//新的结构体变量设置--就是对一个新结构清零初始化
	bzero(&newtio, sizeof(newtio));						//将新结构体置零
	newtio.c_cflag |= CLOCAL | CREAD;					//通常同时使能,确保程序不被其他端口控制和信号干扰,同时串口驱动将读取进入的数据。
	newtio.c_cflag &= ~CSIZE;							//数据位清0
	
	switch(nBits)				//设置数据位--其他位自己设置
	{
		case 7:
			newtio.c_cflag |= CS7;						//七位
			break;
		case 8:
			newtio.c_cflag |= CS8;						//八位
			break;
	}
	
	switch(nEvent)				//设置奇偶校验
	{
		case 'O'://奇校验
			newtio.c_cflag |= PARENB;					//允许校验位
			newtio.c_cflag |= PARODD;					//使用奇校验(清除该标志表示使用偶校验)
			newtio.c_iflag |= (INPCK | ISTRIP);			//启用极性 + 剥去第八位
			break;
		case 'E'://偶校验
			newtio.c_cflag |= PARENB;					//允许校验位
			newtio.c_cflag &= ~PARODD;					//使用偶校验(清除该标志表示使用偶校验)
			newtio.c_iflag |= (INPCK | ISTRIP);			//启用极性 + 剥去第八位
			break;
		case 'N'://无校验
			newtio.c_cflag &= ~PARENB;					//无校验
			break;
	}
	
	switch(nSpeed)				//设置传输速度
	{
		case 2400:
			cfsetispeed(&newtio, B2400);
			cfsetospeed(&newtio, B2400);
			break;
		case 4800:
			cfsetispeed(&newtio, B4800);
			cfsetospeed(&newtio, B4800);
			break;
		case 9600:
			cfsetispeed(&newtio, B9600);
			cfsetospeed(&newtio, B9600);
			break;
		case 115200:
			cfsetispeed(&newtio, B115200);
			cfsetospeed(&newtio, B115200);
			break;
		case 460800:
			cfsetispeed(&newtio, B460800);
			cfsetospeed(&newtio, B460800);
			break;
		default:
			cfsetispeed(&newtio, B9600);
			cfsetospeed(&newtio, B9600);
			break;
	}
	
	if(nStop == 1)				//设置停止位
	{
		newtio.c_cflag &= ~CSTOPB;						//一位停止位
	}
	else if(nStop == 2)
	{
		newtio.c_cflag |= CSTOPB;						//两位停止位
	}
	//如果VTIME=0,VMIN=0,不管能否读取到数据,read都会立即返回
	newtio.c_cc[VTIME] = 0;
	newtio.c_cc[VMIN] = 0;
	tcflush(fd, TCIFLUSH);								//清除所有正在发生的I/O 数据
	//设置生效
	if((tcsetattr(fd, TCSANOW, &newtio)) != 0)			//不等数据传输完毕就立即改变属性
	{
		perror("com set error");
		return -1;
	}
	
	return 0;
}

主函数

int main()
{
	int fd;
	int i = 100, w_length;
	char *uart3 = "/dev/ttySAC3";
	char *test = "Hello world!\n";
	
	if((fd = open(uart3, O_RDWR|O_NDELAY|O_NOCTTY))<0)
	{
		printf("Open %s failed!!!\n", uart3);
	}
	else
	{
		printf("Open %s success!!!\n", uart3);
		set_opt(fd, 115200, 8, 'N', 1);
		while(i--)
		{
			w_length = write(fd, test, strlen(test));
			if(w_length < 0)
			{
				printf("Write buffer error!!!\n");
				exit(1);
			}
			else
			{
				printf("Having writing %d byte.\n",w_length);
			}
			sleep(1);
		}
	}
	close(fd);
}

注意:
这是配置的一般性的过程,如果你在实际中遇到其他的问题,请仔细参看man文档, c_iflag, c_oflag, c_cflag 还涉及很多细小的配置。
比如:
(1):发送字符0X0d的时候,往往接收端得到的字符是0X0a,原因是因为在串口设置中c_iflag和c_oflag中存在从NL-CR和CR-NL的映射,即串口能把回车和换行当成同一个字符,可以进行如下设置屏蔽之:
newtio.c_iflag &= ~ (INLCR | ICRNL | IGNCR);
newtio.c_oflag &= ~(ONLCR | OCRNL);
(2):有时候,在用write发送数据时没有键入回车,信息就发送不出去,这主要是因为我们在输入输出时是按照**规范模式(ICANON )**接收到回车或换行才发送,而更多情况下我们是不必键入回车或换行的。此时应转换到行方式输入,不经处理直接发送,设置如下:
newtio.c_lflag &= ~ (ICANON | ECHO | ECHOE | ISIG);
(3):设置为原始模式传输数据的话,read函数返回的字符数是实际串口收到的字符数。Linux下直接用read读串口可能会造成堵塞,或者数据读出错误,此时可使用fcntl或者select等函数实现异步读取。用select先查询com口,再用read去读就可以避免上述错误。
(4):c_cc数组的VSTART和VSTOP元素被设定成DC1和DC3,代表ASCII标准的XON和XOFF字符(自己百度下),如果在传输这两个字符的时候就传不过去,需要把软件流控制屏蔽,即:
newtio.c_iflag &= ~ (IXON | IXOFF | IXANY);
XON/XOFF(继续/停止)是异步串行连接的计算机和其他元件之间的数据流控制协议。例如,计算机向打印机发送数据的速度通常快于打印机打印的速度,打印机包含一个缓冲器,用来存储数据,使打印机能够赶上计算机。如果在打印机赶上之前缓冲器变满了,打印机的小微处理器便发回一个XOFF信号来停止数据传送,打印完相当多的数据,缓冲存储器变空时,打印机发送XON信号,让计算机继续发送数据。“X”表示“发送器”,X/ON和X/OFF为开启和关闭发送器的信号。X/ON的实际信号为ASCII的Ctrl+Q键盘组合的位组合,X/OFF信号为Ctrl+S字符。
(5):在遇到其他问题我会在补充。

猜你喜欢

转载自blog.csdn.net/weixin_39956356/article/details/86564646