开发环境

开发环境如下图所示:

开发PC:开发PC上运行超级终端,编写、编译串口程序

Tiny6410:运行串口程序,其中/dev/ttySAC0通过串口线与开发PC相连接,/dev/ttySAC3通过串口线与测试PC相连接

测试PC:测试PC上运行串口调试工具,进行串口数据的收发

程序功能描述:Tiny6410通过/dev/ttySAC3串口与测试PC进行串口数据的收发。这个程序是在友善之臂提供的串口应用程序的基础上精简来的。原来的程序看起来有些复杂,初学者读起来有一定难度。经过精简之后,只保留了串口收发数据的关键部分。

源代码

/*==========================================================================
*Name:      uart_S3C6410.c
*Desc:      友善之臂开发板串口收发程序,根据友善之臂提供的串口程序精简而来
*           在超级终端输入要发送的字符,通过串口发送;串口接收的数据由超级
*           终端显示出来。交叉编译后,下载到开发板,执行./uart_S3C6410
*Parameter: 
*Return:    
*Author:    yoyoba(stuyou@126.com)
*Date:      2016-11-03
*Modify:    2016-12-07
*=========================================================================*/

# include <stdio.h>
# include <stdlib.h>
# include <termio.h>
# include <unistd.h>
# include <fcntl.h>
# include <getopt.h>
# include <time.h>
# include <errno.h>
# include <string.h>

//错误消息提示函数
static void Error(const char *Msg)
{
	fprintf (stderr, "%s\n", Msg);
	fprintf (stderr, "strerror() is %s\n", strerror(errno));
	exit(1);
}

//警告消息提示函数
static void Warning(const char *Msg)
{
	fprintf (stderr, "Warning: %s\n", Msg);
}

//使用select监视FD是否可写
static inline void WaitFdWriteable(int Fd)
{
	fd_set WriteSetFD;
	FD_ZERO(&WriteSetFD);
	FD_SET(Fd, &WriteSetFD);
	if (select(Fd + 1, NULL, &WriteSetFD, NULL, NULL) < 0) {
	  Error(strerror(errno));
	} 
}

int main()
{	
	int UART_fd, tty_fd;
	struct termios TtyAttr;
	struct termios BackupTtyAttr;
		
	UART_fd = open("/dev/ttySAC3", O_RDWR, 0);//打开ttySAC3串口
	if (UART_fd < 0)
	  	Error("Unable to open device");
	if (fcntl(UART_fd, F_SETFL, O_NONBLOCK) < 0)//设置串口为非阻塞
	    Error("Unable set to NONBLOCK mode");
         
	memset(&TtyAttr, 0, sizeof(struct termios));//清空ttyattr
	//配置串口:
	TtyAttr.c_iflag = IGNPAR;    //忽略校验
	TtyAttr.c_cflag = B115200 | HUPCL | CS8 | CREAD | CLOCAL;//115200波特率,数据位8,启用接收
	TtyAttr.c_cc[VMIN] = 1; //最小可读取字符数
	if (tcsetattr(UART_fd, TCSANOW, &TtyAttr) < 0)//配置串口
    	Warning("Unable to set comm port");
		
	tty_fd = open("/dev/tty", O_RDWR | O_NDELAY, 0);  //打开终端/dev/tty,当前shell终端
	if (tty_fd < 0)
		Error("Unable to open tty");
	TtyAttr.c_cflag = B115200 | HUPCL | CS8 | CREAD | CLOCAL;//和开发板超级终端属性的配置一致,否则会有问题
	if (tcgetattr(tty_fd, &BackupTtyAttr) < 0)//把终端属性保存在BackupTtyAttr,程序退出时恢复终端使用
		Error("Unable to get tty");
	if (tcsetattr(tty_fd, TCSANOW, &TtyAttr) < 0)//设置终端属性
		Error("Unable to set tty");

	 //启动循环,串口接收和发送数据
	while(1){
		unsigned char buf = 0;
    		fd_set ReadSetFD;

		//select监控串口和终端是否可读
		FD_ZERO(&ReadSetFD);
		FD_SET(UART_fd, &ReadSetFD);
		FD_SET( tty_fd, &ReadSetFD);
		#define max(x,y) ( ((x) >= (y)) ? (x) : (y) )
		if (select(max(UART_fd, tty_fd) + 1, &ReadSetFD, NULL, NULL, NULL) < 0) {
    		Error(strerror(errno));
		}
		#undef max

		//如果串口可读
		if (FD_ISSET(UART_fd, &ReadSetFD)){
			while (read(UART_fd, &buf, 1) == 1) {
				WaitFdWriteable(tty_fd);//等待终端可写
				if (write(tty_fd, &buf, 1) < 0) {//把从串口读取的字符输出到终端显示
     				Error(strerror(errno));
    			}
			}
		}

		//如果终端可读,在终端中输入发送的数据
		if (FD_ISSET(tty_fd, &ReadSetFD)) {
    		while (read(tty_fd, &buf, 1) == 1) {//读取终端中输入的字符
    			static int EscKeyCount = 0;//统计按下ESC键的次数,如果连续按下三次ESC键,则退出程序
    			WaitFdWriteable(UART_fd);//如果串口可写,就通过串口发送
    			if (write(UART_fd, &buf, 1) < 0) {
     				Error(strerror(errno));
    			}
		if(buf != '\x1b')//如果输入的不是ESC键,则在超级终端显示输入的字符
			fwrite((char*)&buf, 1, 1, stderr);//如果不执行这句,超级终端看不到输入的字符
	
		 if (buf == '\x1b') {
                			EscKeyCount ++;
                			if (EscKeyCount >= 3)
                    		goto ExitLabel;
                   // 跳出到退出label
            		} else
                		EscKeyCount = 0;
    		}
  		}
	}
	ExitLabel:
    	if (tcsetattr(tty_fd, TCSANOW, &BackupTtyAttr) < 0)//退出程序之前,恢复超级终端的配置
    		Error("Unable to set tty");

    	return 0;
}

编译

在开发PC上对串口程序进行交叉编译:arm-linux-gcc -o uart_S3C6410 uart_S3C6410.c

运行测试

把开发PC上编译好的uart_S3C6410下载到开发板,并添加执行权限:chmod +x uart_S3C6410,

执行:./uart_S3C6410

1.接收测试:在测试PC的串口调试工具中,发送数据,在开发PC的超级终端中可以看到接收的数据;

2.发送测试:在开发PC的超级终端中输入要发送的数据,在测试PC的串口调试工具中看到接收的数据

3.退出程序:在开发PC超级终端中连续输入三个“ESC”键,则退出程序。