LuckFox Pico Plus 有四个串口,UART2、UART3、UART4 和 UART5,其中 UART2 为调试串口。
随着嵌入式系统应用的发展,Linux操作系统的应用也越来越广泛。Linux作为一款免费的并且开放源代码的操作系统,与Windows操作系统相比有许多独特的优势。Linux可以进行定制内核;Linux的GUI图形界面能够任意选择;Linux可以更方便、更安全地进行远程操作。随着Linux操作系统的不断发展和完善,基于Linux操作系统的软件开发也得到了长足的发展和应用。如果在工控领域引入Linux,不可避免的会遇到在嵌入式Linux下如何实现串行通信的问题。
在Linux操作系统下,对设备和文件的操作都等同于文件的操作,这样大大简化了系统对不同设备的操作,提高了效率。在程序中,设备和文件都是通过文件描述符来操作的。文件描述符是一个非负数的索引值,指向内核中每个进程打开的文件记录表。当打开一个现存的文件或者创建一个新文件时,内核就向进程返回一个文件描述符。当需要对设备进行读写操作时,也需要把文件描述符作为参数传递给相应的函数。
Linux的设备文件都存放在“/dev”目录下,串口资源对应的设备名是“/dev/ttys+编号”,因此串口对应的设备文件的路径是“/dev/ttys*”。而且USB转串口的设备名通常为“/dev/ttyUSB0”,在Linux下对设备的操作方法与对文件的操作方法一样。
在配置完串口的相关属性后,就可以对串口进行打开和读写操作了。它所使用的函数和普通文件的读写函数一样,都是open()、write()和 read()。它们之间的区别的只是串口是一个终端设备,因此在选择函数的具体参数时会有一些区别。另外,这里会用到一些附加的函数,用于测试终端设备的 连接情况等。下面将对其进行具体讲解。
打开串口和打开普通文件一样,都是使用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。
这时,一个串口就已经成功打开了。接下来就可以对这个串口进行读和写操作。
读写串口操作和读写普通文件一样,使用read()和write()函数即可,如下所示:
write(fd, buff, strlen(buff));
read(fd, buff, BUFFER_SIZE);
下面两个实例给出了串口读和写的两个程序,其中用到前面所讲述的open_port()和set_com_config ()函数。写串口的程序将在宿主机上运行,读串口的程序将在目标板上运行。
写串口的程序如下所示。
/*com_writer.c*/
#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;
}
读串口的程序如下所示:
/*com_reader.c*/
#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;
}
/*uart_api.c*/
#include "uart_api.h"
/*
* @函数名:set_com_config
* @函数功能:串口设置函数
*/
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': /* as no parity */
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; /* 读取一个字符等待0*(0/10)s */
new_cfg.c_cc[VMIN] = 1; /* 读取字符的最少个数为0 */
//如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读
tcflush(fd, TCIFLUSH); /* 处理未接收字符 */
if ((tcsetattr(fd, TCSANOW, &new_cfg)) != 0) /* 激活新配置 */
{
perror("tcsetattr action");
return -1;
}
printf("serial set success\n");
return 0;
}
/*
* @函数名:open_port
* @返回值:fd
* @函数功能:打开串口函数
*/
int open_port(char *com_port)
{
int fd;
/*分别为com1,com2, com3对应 ttyUSB0 ttyUSB1 ttyUSB2 */
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;
}
/*
* @函数名:init_port
* @输出参数:com_port
* @返回值: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;
}
/*uart_api.h*/
#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
在开发板上运行写串口的程序,而在目标板上运行读串口的程序。
串口写数据:
串口收数据:
更多回帖