1 LuckFox Pico Plus串口简介
LuckFox Pico Plus 有四个串口,UART2、UART3、UART4 和 UART5,其中 UART2 为调试串口。
2 串口概述
随着嵌入式系统应用的发展,Linux操作系统的应用也越来越广泛。Linux作为一款免费的并且开放源代码的操作系统,与Windows操作系统相比有许多独特的优势。Linux可以进行定制内核;Linux的GUI图形界面能够任意选择;Linux可以更方便、更安全地进行远程操作。随着Linux操作系统的不断发展和完善,基于Linux操作系统的软件开发也得到了长足的发展和应用。如果在工控领域引入Linux,不可避免的会遇到在嵌入式Linux下如何实现串行通信的问题。
在Linux操作系统下,对设备和文件的操作都等同于文件的操作,这样大大简化了系统对不同设备的操作,提高了效率。在程序中,设备和文件都是通过文件描述符来操作的。文件描述符是一个非负数的索引值,指向内核中每个进程打开的文件记录表。当打开一个现存的文件或者创建一个新文件时,内核就向进程返回一个文件描述符。当需要对设备进行读写操作时,也需要把文件描述符作为参数传递给相应的函数。
Linux的设备文件都存放在“/dev”目录下,串口资源对应的设备名是“/dev/ttys+编号”,因此串口对应的设备文件的路径是“/dev/ttys*”。而且USB转串口的设备名通常为“/dev/ttyUSB0”,在Linux下对设备的操作方法与对文件的操作方法一样。
3 串口使用详解
在配置完串口的相关属性后,就可以对串口进行打开和读写操作了。它所使用的函数和普通文件的读写函数一样,都是open()、write()和 read()。它们之间的区别的只是串口是一个终端设备,因此在选择函数的具体参数时会有一些区别。另外,这里会用到一些附加的函数,用于测试终端设备的 连接情况等。下面将对其进行具体讲解。
3.1 打开串口
打开串口和打开普通文件一样,都是使用open()函数,如下所示:
fd = open( "/dev/ttyS0", O_RDWR|O_NOCTTY|O_NDELAY)
可以看到,这里除了普通的读写参数外,还有两个参数O_NOCTTY和O_NDELAY。
O_NOCTTY标志用于通知Linux系统,该参数不会使打开的文件成为这个进程的控制终端。如果没有指定这个标志,那么任何一个输入(诸如键盘中止信号等)都将会影响用户的进程。
O_NDELAY标志通知Linux系统,这个程序不关心DCD信号线所处的状态(端口的另一端是否激活或者停止)。如果用户指定了这个标志,则进程将会一直处在睡眠状态,直到DCD信号线被激活。
接下来可恢复串口的状态为阻塞状态,用于等待串口数据的读入,可用fcntl()函数实现,如下所示:
fcntl(fd, F_SETFL, 0);
再接着可以测试打开文件描述符是否连接到一个终端设备,以进一步确认串口是否正确打开,如下所示:
isatty(STDIN_FILENO);
该函数调用成功则返回0,若失败则返回-1。
这时,一个串口就已经成功打开了。接下来就可以对这个串口进行读和写操作。
3.2 读写串口
读写串口操作和读写普通文件一样,使用read()和write()函数即可,如下所示:
write(fd, buff, strlen(buff))
read(fd, buff, BUFFER_SIZE)
下面两个实例给出了串口读和写的两个程序,其中用到前面所讲述的open_port()和set_com_config ()函数。写串口的程序将在宿主机上运行,读串口的程序将在目标板上运行。
写串口的程序如下所示。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include "uart_api.h"
int main(void)
{
int fd;
char buff[BUFFER_SIZE];
if((fd=open_port(TARGET_COM_PORT))<0)
{
perror("open_port");
return 1;
}
if(set_com_config(fd,115200,8,'N',1)<0)
{
perror("set_com_config error");
return 1;
}
do
{
printf("Input some words(enter 'quit' to exit):");
memset(buff,0,BUFFER_SIZE);
if(fgets(buff,BUFFER_SIZE,stdin)==NULL)
{
perror("fgets");
break;
}
write(fd,buff,strlen(buff));
}while(strncmp(buff,"quit",4));
close(fd);
return 0;
}
读串口的程序如下所示:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include "uart_api.h"
int main(void)
{
int fd;
char buff[BUFFER_SIZE];
if((fd=open_port(TARGET_COM_PORT))<0)
{
perror("open_port");
return 1;
}
if(set_com_config(fd,115200,8,'N',1)<0)
{
perror("set_com_config ");
return 1;
}
do
{
memset(buff,0,BUFFER_SIZE);
if(read(fd,buff,BUFFER_SIZE)>0)
{
printf("the receive words are:%s",buff);
}
}while(strncmp(buff,"quit",4));
close(fd);
return 0;
}
#include "uart_api.h"
int set_com_config(int fd,int baud_rate,int data_bits, char parity, int stop_bits)
{
struct termios new_cfg;
int speed;
if (tcgetattr(fd, &new_cfg) != 0)
{
perror("tcgetattr save");
return -1;
}
new_cfg.c_cflag |= CLOCAL;
new_cfg.c_cflag |= CREAD;
new_cfg.c_oflag &= ~(ONLCR | OCRNL);
new_cfg.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
new_cfg.c_iflag &= ~(ICRNL | INLCR);
new_cfg.c_iflag &= ~(IXON | IXOFF | IXANY);
switch (baud_rate)
{
case 2400:
{
speed = B2400;
}
break;
case 4800:
{
speed = B4800;
}
break;
case 9600:
{
speed = B9600;
}
break;
case 19200:
{
speed = B19200;
}
break;
case 38400:
{
speed = B38400;
}
break;
default:
case 115200:
{
speed = B115200;
}
break;
}
cfsetispeed(&new_cfg, speed);
cfsetospeed(&new_cfg, speed);
switch (data_bits)
{
case 7:
{
new_cfg.c_cflag |= CS7;
}
break;
default:
case 8:
{
new_cfg.c_cflag |= CS8;
}
break;
}
switch (parity)
{
default:
case 'n':
case 'N':
{
new_cfg.c_cflag &= ~PARENB;
new_cfg.c_iflag &= ~INPCK;
}
break;
case 'o':
case 'O':
{
new_cfg.c_cflag |= (PARODD | PARENB);
new_cfg.c_iflag |= INPCK;
}
break;
case 'e':
case 'E':
{
new_cfg.c_cflag |= PARENB;
new_cfg.c_cflag &= ~PARODD;
new_cfg.c_iflag |= INPCK;
}
break;
case 's':
case 'S':
{
new_cfg.c_cflag &= ~PARENB;
new_cfg.c_cflag &= ~CSTOPB;
}
break;
}
switch (stop_bits)
{
default:
case 1:
{
new_cfg.c_cflag &= ~CSTOPB;
}
break;
case 2:
{
new_cfg.c_cflag |= CSTOPB;
}
}
new_cfg.c_oflag &= ~OPOST;
new_cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
new_cfg.c_lflag &= ~(ISIG | ICANON);
new_cfg.c_cc[VTIME] = 0;
new_cfg.c_cc[VMIN] = 1;
tcflush(fd, TCIFLUSH);
if ((tcsetattr(fd, TCSANOW, &new_cfg)) != 0)
{
perror("tcsetattr action");
return -1;
}
printf("serial set success\n");
return 0;
}
int open_port(char *com_port)
{
int fd;
fd = open( com_port, O_RDWR|O_NOCTTY|O_NDELAY);
if (fd < 0)
{
perror("Can't Open Serial Port");
return -1;
}
if (fcntl(fd,F_SETFL,0)<0)
{
perror("fcntl F_SETFL\n");
}
if(isatty(STDIN_FILENO) == 0)
{
perror("standard input is not a terminal device");
}
return fd;
}
int init_port(char *com_port)
{
int fd;
if ((fd = open_port(com_port)) < 0 )
{
perror("open_port");
return -1;
}
printf("open port success\n");
if(set_com_config(fd,115200,8,'N',1) < 0)
{
perror("set_com_config");
return -1;
}
return fd;
}
#ifndef UART_API_H
#define UART_API_H
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include <termios.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <ctype.h>
#define BUFFER_SIZE 36
#define TARGET_COM_PORT "/dev/ttyS3"
int set_com_config(int fd,int baud_rate, int data_bits,char parity,int stop_bits);
int open_port(char *com_port);
int init_port(char *com_port);
#endif
在开发板上运行写串口的程序,而在目标板上运行读串口的程序。
串口写数据:
串口收数据: